2019-06-24————异常日志

https://blog.csdn.net/f4761/article/details/86589600

Spring Boot -日志配置加载流程
https://www.jianshu.com/p/04ce5d85eeb8

日志在application下可以打印,在tomat下无法打印
猜测:根据日志文件,tomcat的日志下存在jdk的日志-Djava.util.logging.config.file=/home/bianla_jxs/eggServer/conf/logging.properties。猜测是tomcat选择了java的log。

debug发现:在初始化的时候2次都是从(environment)载入jdk的log。而application是没有env的,所以通过url载入资源。
【首选是env】,所以应该是先把env中的配置修改,项目用的配置文件是yml,所以应该考虑如何把yml加入到env中。

查询。springboot如何加载springboot
1:学框架至少先把入口学明白。【SpringApplication.run】
https://blog.csdn.net/chengkui1990/article/details/79866499

然后再debug的时候,发现系统启动的时候会先调用LoggingApplicationListener,然后初始化spring,配置env,在调用一遍LoggingApplicationListener

1:springboot也会报错:2:在启动代里也有log方法,所以在启动前肯定也有配置日志的地方。

容器启动时的初始化环境的环节。
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
发现是在生成监听器的时候就有log,再发现有2个logfactory。

查找另一个的来源
错误:log虽然是抽象类类产生,但是是抽象类的静态方法,所以没问题。
但是实例一定是要类的,所以进去查看抽象类中的静态方法是如何产生实例的。发现是一个抽象方法。然后查看这个抽象方法是如何产


springboot,启动接口进入以后,在方法最后打断点,发现执行到段点的时候是容器启动完。
Deployment of web application directory G:\apache-tomcat-7.0.88\webapps\manager has finished in 214 ms,
同事通过断点发现传参没有用,关于传参,这是一个规范,可以参考

然后springboot在LoggingApplicationListener初始化方法中的断点再次停下。


https://blog.csdn.net/lz710117239/article/details/80444200
spring的SpringApplication.run()执行两遍的问题


https://www.jianshu.com/p/04ce5d85eeb8
一个项目可以用多个日志,springboot可能考虑到没有后期日志框架,所以默认使用common.apache中的日志文件,没毛病。

在入口处断点,在监听器开始的时候,会执行log的监听器LoggingApplicationListener中的onApplicationEvent方法。

在onApplicationStartingEvent日志初始化前置操作的时候loggingSystem的类为log4j2的实现类。

环境资源加载完成事件,初始化LoggingSystem时,在LoggingApplicationListener的initialize方法中查找配置。

【重点】这里忽略了自定义的配置

private void initializeSystem(ConfigurableEnvironment environment, LoggingSystem system, LogFile logFile) {
        LoggingInitializationContext initializationContext = new LoggingInitializationContext(environment);
        String logConfig = environment.getProperty("logging.config");
        //是否忽略日志配置,采用自定义配置  
        if (this.ignoreLogConfig(logConfig)) {
            system.initialize(initializationContext, (String)null, logFile);
        } else {
            try {
                ResourceUtils.getURL(logConfig).openStream().close();
                system.initialize(initializationContext, logConfig, logFile);
            } catch (Exception var7) {
                System.err.println("Logging system failed to initialize using configuration from '" + logConfig + "'");
                var7.printStackTrace(System.err);
                throw new IllegalStateException(var7);
            }
        }

    }

内部方法索命如果logConfig为空或者-D开头,忽略。同时打断点发现,logCinfig是
-Djava.util.logging.config.file="C:\Users...\logging.properties"
从环境enviroment中获取

    private boolean ignoreLogConfig(String logConfig) {
        return !StringUtils.hasLength(logConfig) || logConfig.startsWith("-D");
    }

这里走了很多弯路,其实早就找到了,但是没仔细看,导致理解错了这个判断的意思。至此,需要查看环境中的配置是哪来的,并做修改。

猜测是来自tomcat容器,但是理论上应该可以通过yml初始化。但是这里还是要先从根本enviroment的实例开始
https://fangjian0423.github.io/2017/06/10/springboot-environment-analysis/

ConfigFileApplicationListener的doLoadIntoGroup(464行)断点的时候(第二次,第一次是application。yml中 的数据),发现propertySource中有logging.config=applicationConfig: [classpath:/config/application-dev.yml],是需要的数据。

【也可能】是yml直接加在到propertySource,然后从注解@propertySource中获取。

LoggingApplicationListener中的logging.config来自environment.getProperty("logging.config");所以要查environment来源,一步一步往上查找(不要直接从入口找,可能不是同一个环境),发现是从ApplicationEnvironmentPreparedEvent中getEnvironment。然后在onApplicationEvent中调用。这个方法继承自父类ApplicationListener。

在启动接口处的下面两段代码中加入2个断点,发现执行第一个断点厚跳入内部上述代码,结束玩以后执行第二个断点。

            ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
            Banner printedBanner = this.printBanner(environment);

猜测最终的Environment实现类可能是StandardServletEnvironment

prepareEnvironment是预备环境,在这个方法里已经有了配置的文件属性。同时具体的实现方面里面的getOrCreateEnvironment,生成一个没什么用的初始实例。

在SpringApplication的入口方法中的
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
中的prepareEnvironment中的
listeners.environmentPrepared((ConfigurableEnvironment)environment);
打断点的时候发现,listens中的application中的listens中有log监听器,同时传入外部的StandardServletEnvironment
作为参数给这些监听器。
同时根据排序发现,
第一个是ConfigFileApplicationListener监听器,
第三个是LoggingApplicationListener监听器,
所以是先用config监听机加载配置文件后,再调用log监听器


StandardServletEnvironment的getProperty来自父类AbstractEnvironment中的PropertySourcesPropertyResolver实例的getProperty。

断点先走到LoggingApplicationListener的获取logging.config部分,再在PropertySourcesPropertyResolver的getProperty打断点。


图片.png

发现logging.config属性为空。同时propertySource的属性为servletConfigInitParams,只能说明在这个内容里面没有值,【因为外面有一层循环,会在var4里面一个个获取】,继续断点到systemEnvironment,发现里面有了值,为-Djava.util.logging.config.file="C:...\logging.properties"。
【重点】看代码发现,在找到上述值以后,不会继续循环,而是会赋值以后直接结束该循环。

解决:要么修改这个参数,要么在之前的属性里面添加logging.config的参数。但是上面的不可以加

【重点】突然发现之前的思路又是错的。日志的使用并不是通过系统中的日志,如果是的话,应该走上面的思路,但实际上是通过LoggerFactory.getLogger()实现,所以思路应该是查看这个getlogger是怎么获取logger的。

    public static Logger getLogger(String name) {
        ILoggerFactory iLoggerFactory = getILoggerFactory();
        return iLoggerFactory.getLogger(name);
    }

所以getILoggerFactory得到是一个Log4jLoggerFactory实例,而Log4jLoggerFactory没有getLogger的方法,所以跳到父类AbstractLoggerAdapter<Logger>,父类新建一个个logger【Log4jLogger】并放到loggers中。然后由Log4jLogger打印日志。然后....断思路
PS:这里要强调下目的,【研究LoggerFactory.getLogger是如何获取实例的】

重启思路:在错误点打断点,发现logger是log4j的实例,说明只是没输出。实例是存在的。
再次往里面跳,在最后的AbstractLogger的this.logMessage无法跳到实现。因为该类是抽象类,实现的父接口的接口,所以具体实现由具体继承的子类来实现。

回想application的时候能输出日志到.log文件,说明文件就是在初始化的时候生成的。所以问题还是出在初始化的时候。


通过application的断点,确定是system.initialize(initializationContext, logConfig, logFile);这串代码生成了日志文件。

application

tomcat

发现:输出日志也会有个多个,本质是还是不懂日志模块。
【网上给出的答案】https://www.jianshu.com/p/7543a2cde78d【学习思路】


弄懂了外置和内置tomcat的区别以后,在入口的configure方法中加入

        System.setProperty("logging.config","classpath:config/log4j2-spring.xml");
        System.setProperty("logging.manager","org.apache.logging.log4j.LogManager");

在logger初始化的时候正确,但是依旧没有生成日志文件

以外发现:
想找最后生成生成文件的类,但是找不到。无意间在配置文件里面写错路径,报错。根据异常栈找到了最后的类。断点发现已经产生父类。但是路径未知。再次通过项目新建一个file,然后获取绝对路径,发现日志打印在了tomcat下的bin文件内。

https://www.jianshu.com/p/04ce5d85eeb8
可参考
PS:网上说springboot2.0没事,但是当前spring1.5.X会有问题。当前版本【1.5.7】

推荐阅读更多精彩内容