关于logback:logback日志级别动态调整

最近用到,spring boot的日志组件logback,将通用的一些配置和操作分享一下 日志级别动静调整,次要是我的项目运行期间做日志降级,排查问题调整日志级别等 交互就依照传统的spring,做个demo: 核心思想:ch.qos.logback.classic.LoggerContextlogback能够通过LoggerContext获取所有的logger;logback能够通过ch.qos.logback.classic.Logger.setLevel(ch.qos.logback.classic.Level);管制打印的日志级别 这里不废话了,间接贴源码: Controller层源码 import ch.qos.logback.classic.Level;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.http.HttpStatus;import org.springframework.http.ResponseEntity;import org.springframework.web.bind.annotation.*;import java.util.ArrayList;import java.util.List;/** * 日志级别controller */@RestController@RequestMapping(value = "/log/level")@Slf4jpublic class LogLevelController { /** * 日志级别服务 */ @Autowired private LogLevelService logLevelService; /** * 获取日志级别列表 * * @return 日志级别信息 */ @RequestMapping(method = RequestMethod.GET, produces = "application/json") @ResponseBody public ResponseEntity<List<LogLevelDto>> getLogLevelList() { List<LogLevelDto> logLevelDtoList = new ArrayList<>(); logLevelService.getLogLevel().stream().forEach(item -> { logLevelDtoList.add(new LogLevelDto(item.getName(), null == item.getLevel() ? "" : item.getLevel().levelStr)); }); ResponseEntity<List<LogLevelDto>> response = new ResponseEntity<List<LogLevelDto>>(logLevelDtoList, HttpStatus.OK); log.info("res:{}", response); return response; } /** * 设置日志级别 * * @param logLevelDto 日志级别 * @return 设置后果 */ @RequestMapping(method = RequestMethod.POST, produces = "application/json", consumes = "application/json") @ResponseBody public ResponseEntity<Void> setLogLevel(@RequestBody LogLevelDto logLevelDto) { LogLevelPojo logLevelPojo = new LogLevelPojo(logLevelDto.getName(), Level.toLevel(logLevelDto.getLevel())); logLevelService.setLogLevel(logLevelPojo); return new ResponseEntity<>(null, HttpStatus.OK); }}Service层源码 ...

October 19, 2022 · 2 min · jiezi

springboot2启动信息去除CONDITIONS-EVALUATION-REPORT后引发的思考

前言最近启动一个springboot2.2+版本我的项目时,发现控制台输入 17:22:05 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.cloud.util.random.CachedRandomPropertySourceAutoConfiguration'17:22:05 [main] DEBUG o.s.b.a.l.ConditionEvaluationReportLoggingListener - ============================CONDITIONS EVALUATION REPORT============================Positive matches:----------------- ConfigurationPropertiesRebinderAutoConfiguration matched: - @ConditionalOnBean (types: org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor; SearchStrategy: all) found bean 'org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor' (OnBeanCondition) ConfigurationPropertiesRebinderAutoConfiguration#configurationPropertiesBeans matched: - @ConditionalOnMissingBean (types: org.springframework.cloud.context.properties.ConfigurationPropertiesBeans; SearchStrategy: current) did not find any beans (OnBeanCondition) ConfigurationPropertiesRebinderAutoConfiguration#configurationPropertiesRebinder matched: - @ConditionalOnMissingBean (types: org.springframework.cloud.context.properties.ConfigurationPropertiesRebinder; SearchStrategy: current) did not find any beans (OnBeanCondition) EncryptionBootstrapConfiguration matched: - @ConditionalOnClass found required class 'org.springframework.security.crypto.encrypt.TextEncryptor' (OnClassCondition) PropertyPlaceholderAutoConfiguration#propertySourcesPlaceholderConfigurer matched: - @ConditionalOnMissingBean (types: org.springframework.context.support.PropertySourcesPlaceholderConfigurer; SearchStrategy: current) did not find any beans (OnBeanCondition)Negative matches:----------------- EncryptionBootstrapConfiguration.RsaEncryptionConfiguration: Did not match: - Keystore nor key found in Environment (EncryptionBootstrapConfiguration.KeyCondition) Matched: - @ConditionalOnClass found required class 'org.springframework.security.rsa.crypto.RsaSecretEncryptor' (OnClassCondition) EncryptionBootstrapConfiguration.VanillaEncryptionConfiguration: Did not match: - @ConditionalOnMissingClass found unwanted class 'org.springframework.security.rsa.crypto.RsaSecretEncryptor' (OnClassCondition) EurekaDiscoveryClientConfigServiceBootstrapConfiguration: Did not match: - @ConditionalOnClass did not find required class 'org.springframework.cloud.config.client.ConfigServicePropertySourceLocator' (OnClassCondition)Exclusions:----------- NoneUnconditional classes:---------------------- None17:22:13 [main] DEBUG o.s.b.c.l.ClasspathLoggingApplicationListener - Application started with classpath: [file:/F:/soft/jdk/jdk1.8.0_241/jre/lib/charsets.jar, file:/F:/soft/jdk/jdk1.8.0_241/jre/lib/deploy.jar, file:/F:/soft/jdk/jdk1.8.0_241/jre/lib/ext/access-bridge-64.jar, file:/F:/soft/jdk/jdk1.8.0_241/jre/lib/ext/cldrdata.jar, file:/F:/soft/jdk/jdk1.8.0_241/jre/lib/ext/dnsns.jar, file:/F:/soft/jdk/jdk1.8.0_241/jre/lib/ext/jaccess.jar, file:/F:/soft/jdk/jdk1.8.0_241/jre/lib/ext/jfxrt.jar, file:/F:/soft/jdk/jdk1.8.0_241/jre/lib/ext/localedata.jar, file:/F:/soft/jdk/jdk1.8.0_241/jre/lib/ext/nashorn.jar, file:/F:/soft/jdk/jdk1.8.0_241/jre/lib/ext/sunec.jar, file:/F:/soft/jdk/jdk1.8.0_241/jre/lib/ext/sunjce_provider.jar, file:/F:/soft/jdk/jdk1.8.0_241/jre/lib/ext/sunmscapi.jar, file:/F:/soft/jdk/jdk1.8.0_241/jre/lib/ext/sunpkcs11.jar, file:/F:/soft/jdk/jdk1.8.0_241/jre/lib/ext/zipfs.jar, file:/F:/soft/jdk/jdk1.8.0_241/jre/lib/javaws.jar, file:/F:/soft/jdk/jdk1.8.0_241/jre/lib/jce.jar, file:/F:/soft/jdk/jdk1.8.0_241/jre/lib/jfr.jar, file:/F:/soft/jdk/jdk1.8.0_241/jre/lib/jfxswt.jar, file:/F:/soft/jdk/jdk1.8.0_241/jre/lib/jsse.jar, file:/F:/soft/jdk/jdk1.8.0_241/jre/lib/management-agent.jar, file:/F:/soft/jdk/jdk1.8.0_241/jre/lib/plugin.jar, file:/F:/soft/jdk/jdk1.8.0_241/jre/lib/resources.jar, file:/F:/soft/jdk/jdk1.8.0_241/jre/lib/rt.jar, file:/E:/nisbos-cloud/nisbos-springcloud/nisbos-springcloud-example/nisbos-springcloud-example-goods/nisbos-springcloud-example-goods-biz/target/classes/, file:/D:/repository/org/springframework/boot/spring-boot-starter/2.2.5.RELEASE/spring-boot-starter-2.2.5.RELEASE.jar, file:/D:/repository/org/springframework/boot/spring-boot/2.2.5.RELEASE/spring-boot-2.2.5.RELEASE.jar, file:/D:/repository/org/springframework/boot/spring-boot-starter-logging/2.2.5.RELEASE/spring-boot-starter-logging-2.2.5.RELEASE.jar, file:/D:/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar, file:/D:/repository/ch/qos/logback/logback-core/1.2.3/logback-core-1.2.3.jar, file:/D:/repository/org/apache/logging/log4j/log4j-to-slf4j/2.12.1/log4j-to-slf4j-2.12.1.jar, file:/D:/repository/org/apache/logging/log4j/log4j-api/2.12.1/log4j-api-2.12.1.jar, file:/D:/repository/org/slf4j/jul-to-slf4j/1.7.30/jul-to-slf4j-1.7.30.jar, file:/D:/repository/jakarta/annotation/jakarta.annotation-api/1.3.5/jakarta.annotation-api-1.3.5.jar, file:/D:/repository/org/yaml/snakeyaml/1.25/snakeyaml-1.25.jar, file:/D:/repository/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar, file:/D:/repository/org/springframework/spring-core/5.2.4.RELEASE/spring-core-5.2.4.RELEASE.jar, file:/D:/repository/org/springframework/spring-jcl/5.2.4.RELEASE/spring-jcl-5.2.4.RELEASE.jar, file:/D:/repository/org/springframework/boot/spring-boot-starter-web/2.2.5.RELEASE/spring-boot-starter-web-2.2.5.RELEASE.jar, file:/D:/repository/org/springframework/boot/spring-boot-starter-json/2.2.5.RELEASE/spring-boot-starter-json-2.2.5.RELEASE.jar, file:/D:/repository/com/fasterxml/jackson/core/jackson-databind/2.10.2/jackson-databind-2.10.2.jar, file:/D:/repository/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.10.2/jackson-datatype-jdk8-2.10.2.jar, file:/D:/repository/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.10.2/jackson-datatype-jsr310-2.10.2.jar, file:/D:/repository/com/fasterxml/jackson/module/jackson-module-parameter-names/2.10.2/jackson-module-parameter-names-2.10.2.jar, file:/D:/repository/org/springframework/boot/spring-boot-starter-tomcat/2.2.5.RELEASE/spring-boot-starter-tomcat-2.2.5.RELEASE.jar, file:/D:/repository/org/apache/tomcat/embed/tomcat-embed-core/9.0.31/tomcat-embed-core-9.0.31.jar, file:/D:/repository/org/apache/tomcat/embed/tomcat-embed-el/9.0.31/tomcat-embed-el-9.0.31.jar, file:/D:/repository/org/apache/tomcat/embed/tomcat-embed-websocket/9.0.31/tomcat-embed-websocket-9.0.31.jar, file:/D:/repository/org/springframework/boot/spring-boot-starter-validation/2.2.5.RELEASE/spring-boot-starter-validation-2.2.5.RELEASE.jar, file:/D:/repository/jakarta/validation/jakarta.validation-api/2.0.2/jakarta.validation-api-2.0.2.jar, file:/D:/repository/org/hibernate/validator/hibernate-validator/6.0.18.Final/hibernate-validator-6.0.18.Final.jar, file:/D:/repository/org/jboss/logging/jboss-logging/3.4.1.Final/jboss-logging-3.4.1.Final.jar, file:/D:/repository/com/fasterxml/classmate/1.5.1/classmate-1.5.1.jar, file:/D:/repository/org/springframework/spring-web/5.2.4.RELEASE/spring-web-5.2.4.RELEASE.jar, file:/D:/repository/org/springframework/spring-beans/5.2.4.RELEASE/spring-beans-5.2.4.RELEASE.jar, file:/D:/repository/org/springframework/spring-webmvc/5.2.4.RELEASE/spring-webmvc-5.2.4.RELEASE.jar, file:/D:/repository/org/springframework/spring-aop/5.2.4.RELEASE/spring-aop-5.2.4.RELEASE.jar, file:/D:/repository/org/springframework/spring-context/5.2.4.RELEASE/spring-context-5.2.4.RELEASE.jar, file:/D:/repository/org/springframework/spring-expression/5.2.4.RELEASE/spring-expression-5.2.4.RELEASE.jar, file:/E:/nisbos-cloud/nisbos-springcloud/nisbos-springcloud-example/nisbos-springcloud-example-goods/nisbos-springcloud-example-goods-api/target/classes/, file:/D:/repository/org/springframework/cloud/spring-cloud-starter-openfeign/2.2.2.RELEASE/spring-cloud-starter-openfeign-2.2.2.RELEASE.jar, file:/D:/repository/org/springframework/cloud/spring-cloud-openfeign-core/2.2.2.RELEASE/spring-cloud-openfeign-core-2.2.2.RELEASE.jar, file:/D:/repository/io/github/openfeign/form/feign-form-spring/3.8.0/feign-form-spring-3.8.0.jar, file:/D:/repository/io/github/openfeign/form/feign-form/3.8.0/feign-form-3.8.0.jar, file:/D:/repository/commons-fileupload/commons-fileupload/1.4/commons-fileupload-1.4.jar, file:/D:/repository/commons-io/commons-io/2.6/commons-io-2.6.jar, file:/D:/repository/org/springframework/cloud/spring-cloud-commons/2.2.2.RELEASE/spring-cloud-commons-2.2.2.RELEASE.jar, file:/D:/repository/io/github/openfeign/feign-core/10.7.4/feign-core-10.7.4.jar, file:/D:/repository/io/github/openfeign/feign-slf4j/10.7.4/feign-slf4j-10.7.4.jar, file:/D:/repository/io/github/openfeign/feign-hystrix/10.7.4/feign-hystrix-10.7.4.jar, file:/E:/nisbos-cloud/nisbos-springcloud/nisbos-springcloud-common/nisbos-rpc-framework/target/classes/, file:/E:/nisbos-cloud/nisbos-springcloud/nisbos-springcloud-common/nisbos-common-trace/target/classes/, file:/D:/repository/org/springframework/boot/spring-boot-configuration-processor/2.2.5.RELEASE/spring-boot-configuration-processor-2.2.5.RELEASE.jar, file:/D:/repository/com/dianping/cat/cat-client/3.0.0/cat-client-3.0.0.jar, file:/D:/repository/com/ctrip/framework/apollo/apollo-client/1.6.0/apollo-client-1.6.0.jar, file:/D:/repository/com/ctrip/framework/apollo/apollo-core/1.6.0/apollo-core-1.6.0.jar, file:/D:/repository/com/google/code/gson/gson/2.8.6/gson-2.8.6.jar, file:/D:/repository/org/mybatis/mybatis/3.5.3/mybatis-3.5.3.jar, file:/E:/nisbos-cloud/nisbos-springcloud/nisbos-springcloud-example/nisbos-springcloud-example-common/target/classes/, file:/D:/repository/org/apache/shiro/shiro-core/1.5.3/shiro-core-1.5.3.jar, file:/D:/repository/org/apache/shiro/shiro-lang/1.5.3/shiro-lang-1.5.3.jar, file:/D:/repository/org/apache/shiro/shiro-cache/1.5.3/shiro-cache-1.5.3.jar, file:/D:/repository/org/apache/shiro/shiro-crypto-hash/1.5.3/shiro-crypto-hash-1.5.3.jar, file:/D:/repository/org/apache/shiro/shiro-crypto-core/1.5.3/shiro-crypto-core-1.5.3.jar, file:/D:/repository/org/apache/shiro/shiro-crypto-cipher/1.5.3/shiro-crypto-cipher-1.5.3.jar, file:/D:/repository/org/apache/shiro/shiro-config-core/1.5.3/shiro-config-core-1.5.3.jar, file:/D:/repository/org/apache/shiro/shiro-config-ogdl/1.5.3/shiro-config-ogdl-1.5.3.jar, file:/D:/repository/commons-beanutils/commons-beanutils/1.9.4/commons-beanutils-1.9.4.jar, file:/D:/repository/org/apache/shiro/shiro-event/1.5.3/shiro-event-1.5.3.jar, file:/D:/repository/commons-lang/commons-lang/2.6/commons-lang-2.6.jar, file:/D:/repository/org/apache/commons/commons-lang3/3.9/commons-lang3-3.9.jar, file:/D:/repository/com/baomidou/mybatis-plus-boot-starter/3.3.1/mybatis-plus-boot-starter-3.3.1.jar, file:/D:/repository/com/baomidou/mybatis-plus/3.3.1/mybatis-plus-3.3.1.jar, file:/D:/repository/com/baomidou/mybatis-plus-extension/3.3.1/mybatis-plus-extension-3.3.1.jar, file:/D:/repository/com/baomidou/mybatis-plus-core/3.3.1/mybatis-plus-core-3.3.1.jar, file:/D:/repository/com/baomidou/mybatis-plus-annotation/3.3.1/mybatis-plus-annotation-3.3.1.jar, file:/D:/repository/com/github/jsqlparser/jsqlparser/3.1/jsqlparser-3.1.jar, file:/D:/repository/org/mybatis/mybatis-spring/2.0.3/mybatis-spring-2.0.3.jar, file:/D:/repository/org/springframework/boot/spring-boot-autoconfigure/2.2.5.RELEASE/spring-boot-autoconfigure-2.2.5.RELEASE.jar, file:/D:/repository/org/springframework/boot/spring-boot-starter-jdbc/2.2.5.RELEASE/spring-boot-starter-jdbc-2.2.5.RELEASE.jar, file:/D:/repository/com/zaxxer/HikariCP/3.4.2/HikariCP-3.4.2.jar, file:/D:/repository/org/springframework/spring-jdbc/5.2.4.RELEASE/spring-jdbc-5.2.4.RELEASE.jar, file:/D:/repository/org/springframework/spring-tx/5.2.4.RELEASE/spring-tx-5.2.4.RELEASE.jar, file:/D:/repository/mysql/mysql-connector-java/8.0.19/mysql-connector-java-8.0.19.jar, file:/D:/repository/com/alibaba/druid/1.0.28/druid-1.0.28.jar, file:/F:/soft/jdk/jdk1.8.0_241/lib/jconsole.jar, file:/F:/soft/jdk/jdk1.8.0_241/lib/tools.jar, file:/D:/repository/org/springframework/cloud/spring-cloud-starter-netflix-eureka-client/2.2.2.RELEASE/spring-cloud-starter-netflix-eureka-client-2.2.2.RELEASE.jar, file:/D:/repository/org/springframework/cloud/spring-cloud-starter/2.2.2.RELEASE/spring-cloud-starter-2.2.2.RELEASE.jar, file:/D:/repository/org/springframework/security/spring-security-rsa/1.0.9.RELEASE/spring-security-rsa-1.0.9.RELEASE.jar, file:/D:/repository/org/bouncycastle/bcpkix-jdk15on/1.64/bcpkix-jdk15on-1.64.jar, file:/D:/repository/org/bouncycastle/bcprov-jdk15on/1.64/bcprov-jdk15on-1.64.jar, file:/D:/repository/org/springframework/cloud/spring-cloud-netflix-hystrix/2.2.2.RELEASE/spring-cloud-netflix-hystrix-2.2.2.RELEASE.jar, file:/D:/repository/org/springframework/boot/spring-boot-starter-aop/2.2.5.RELEASE/spring-boot-starter-aop-2.2.5.RELEASE.jar, file:/D:/repository/org/springframework/cloud/spring-cloud-netflix-eureka-client/2.2.2.RELEASE/spring-cloud-netflix-eureka-client-2.2.2.RELEASE.jar, file:/D:/repository/com/netflix/eureka/eureka-client/1.9.17/eureka-client-1.9.17.jar, file:/D:/repository/org/codehaus/jettison/jettison/1.3.7/jettison-1.3.7.jar, file:/D:/repository/stax/stax-api/1.0.1/stax-api-1.0.1.jar, file:/D:/repository/com/netflix/netflix-commons/netflix-eventbus/0.3.0/netflix-eventbus-0.3.0.jar, file:/D:/repository/com/netflix/netflix-commons/netflix-infix/0.3.0/netflix-infix-0.3.0.jar, file:/D:/repository/commons-jxpath/commons-jxpath/1.3/commons-jxpath-1.3.jar, file:/D:/repository/joda-time/joda-time/2.10.5/joda-time-2.10.5.jar, file:/D:/repository/org/antlr/antlr-runtime/3.4/antlr-runtime-3.4.jar, file:/D:/repository/org/antlr/stringtemplate/3.2.1/stringtemplate-3.2.1.jar, file:/D:/repository/antlr/antlr/2.7.7/antlr-2.7.7.jar, file:/D:/repository/org/apache/commons/commons-math/2.2/commons-math-2.2.jar, file:/D:/repository/com/netflix/archaius/archaius-core/0.7.6/archaius-core-0.7.6.jar, file:/D:/repository/javax/ws/rs/jsr311-api/1.1.1/jsr311-api-1.1.1.jar, file:/D:/repository/com/netflix/servo/servo-core/0.12.21/servo-core-0.12.21.jar, file:/D:/repository/com/sun/jersey/jersey-core/1.19.1/jersey-core-1.19.1.jar, file:/D:/repository/com/sun/jersey/jersey-client/1.19.1/jersey-client-1.19.1.jar, file:/D:/repository/com/sun/jersey/contribs/jersey-apache-client4/1.19.1/jersey-apache-client4-1.19.1.jar, file:/D:/repository/org/apache/httpcomponents/httpclient/4.5.12/httpclient-4.5.12.jar, file:/D:/repository/org/apache/httpcomponents/httpcore/4.4.13/httpcore-4.4.13.jar, file:/D:/repository/commons-codec/commons-codec/1.14/commons-codec-1.14.jar, file:/D:/repository/com/google/inject/guice/4.1.0/guice-4.1.0.jar, file:/D:/repository/javax/inject/javax.inject/1/javax.inject-1.jar, file:/D:/repository/aopalliance/aopalliance/1.0/aopalliance-1.0.jar, file:/D:/repository/com/fasterxml/jackson/core/jackson-annotations/2.10.2/jackson-annotations-2.10.2.jar, file:/D:/repository/com/fasterxml/jackson/core/jackson-core/2.10.2/jackson-core-2.10.2.jar, file:/D:/repository/com/netflix/eureka/eureka-core/1.9.17/eureka-core-1.9.17.jar, file:/D:/repository/com/fasterxml/woodstox/woodstox-core/5.0.3/woodstox-core-5.0.3.jar, file:/D:/repository/org/codehaus/woodstox/stax2-api/3.1.4/stax2-api-3.1.4.jar, file:/D:/repository/org/springframework/cloud/spring-cloud-starter-netflix-archaius/2.2.2.RELEASE/spring-cloud-starter-netflix-archaius-2.2.2.RELEASE.jar, file:/D:/repository/org/springframework/cloud/spring-cloud-netflix-archaius/2.2.2.RELEASE/spring-cloud-netflix-archaius-2.2.2.RELEASE.jar, file:/D:/repository/commons-configuration/commons-configuration/1.10/commons-configuration-1.10.jar, file:/D:/repository/org/springframework/cloud/spring-cloud-starter-netflix-ribbon/2.2.2.RELEASE/spring-cloud-starter-netflix-ribbon-2.2.2.RELEASE.jar, file:/D:/repository/com/netflix/ribbon/ribbon/2.3.0/ribbon-2.3.0.jar, file:/D:/repository/com/netflix/ribbon/ribbon-transport/2.3.0/ribbon-transport-2.3.0.jar, file:/D:/repository/io/reactivex/rxnetty-contexts/0.4.9/rxnetty-contexts-0.4.9.jar, file:/D:/repository/io/reactivex/rxnetty-servo/0.4.9/rxnetty-servo-0.4.9.jar, file:/D:/repository/io/reactivex/rxnetty/0.4.9/rxnetty-0.4.9.jar, file:/D:/repository/com/netflix/ribbon/ribbon-core/2.3.0/ribbon-core-2.3.0.jar, file:/D:/repository/com/netflix/ribbon/ribbon-httpclient/2.3.0/ribbon-httpclient-2.3.0.jar, file:/D:/repository/commons-collections/commons-collections/3.2.2/commons-collections-3.2.2.jar, file:/D:/repository/com/netflix/netflix-commons/netflix-commons-util/0.3.0/netflix-commons-util-0.3.0.jar, file:/D:/repository/com/netflix/ribbon/ribbon-loadbalancer/2.3.0/ribbon-loadbalancer-2.3.0.jar, file:/D:/repository/com/netflix/netflix-commons/netflix-statistics/0.1.1/netflix-statistics-0.1.1.jar, file:/D:/repository/io/reactivex/rxjava/1.3.8/rxjava-1.3.8.jar, file:/D:/repository/org/springframework/cloud/spring-cloud-starter-loadbalancer/2.2.2.RELEASE/spring-cloud-starter-loadbalancer-2.2.2.RELEASE.jar, file:/D:/repository/org/springframework/cloud/spring-cloud-loadbalancer/2.2.2.RELEASE/spring-cloud-loadbalancer-2.2.2.RELEASE.jar, file:/D:/repository/io/projectreactor/reactor-core/3.3.3.RELEASE/reactor-core-3.3.3.RELEASE.jar, file:/D:/repository/io/projectreactor/addons/reactor-extra/3.3.2.RELEASE/reactor-extra-3.3.2.RELEASE.jar, file:/D:/repository/org/springframework/boot/spring-boot-starter-cache/2.2.5.RELEASE/spring-boot-starter-cache-2.2.5.RELEASE.jar, file:/D:/repository/org/springframework/spring-context-support/5.2.4.RELEASE/spring-context-support-5.2.4.RELEASE.jar, file:/D:/repository/com/stoyanr/evictor/1.0.0/evictor-1.0.0.jar, file:/D:/repository/com/netflix/ribbon/ribbon-eureka/2.3.0/ribbon-eureka-2.3.0.jar, file:/D:/repository/com/thoughtworks/xstream/xstream/1.4.12/xstream-1.4.12.jar, file:/D:/repository/xmlpull/xmlpull/1.1.3.1/xmlpull-1.1.3.1.jar, file:/D:/repository/xpp3/xpp3_min/1.1.4c/xpp3_min-1.1.4c.jar, file:/D:/repository/org/springframework/cloud/spring-cloud-starter-netflix-hystrix/2.2.2.RELEASE/spring-cloud-starter-netflix-hystrix-2.2.2.RELEASE.jar, file:/D:/repository/org/springframework/cloud/spring-cloud-netflix-ribbon/2.2.2.RELEASE/spring-cloud-netflix-ribbon-2.2.2.RELEASE.jar, file:/D:/repository/com/netflix/hystrix/hystrix-core/1.5.18/hystrix-core-1.5.18.jar, file:/D:/repository/org/hdrhistogram/HdrHistogram/2.1.9/HdrHistogram-2.1.9.jar, file:/D:/repository/com/netflix/hystrix/hystrix-serialization/1.5.18/hystrix-serialization-1.5.18.jar, file:/D:/repository/com/fasterxml/jackson/module/jackson-module-afterburner/2.10.2/jackson-module-afterburner-2.10.2.jar, file:/D:/repository/com/netflix/hystrix/hystrix-metrics-event-stream/1.5.18/hystrix-metrics-event-stream-1.5.18.jar, file:/D:/repository/com/netflix/hystrix/hystrix-javanica/1.5.18/hystrix-javanica-1.5.18.jar, file:/D:/repository/org/ow2/asm/asm/5.0.4/asm-5.0.4.jar, file:/D:/repository/org/aspectj/aspectjweaver/1.9.5/aspectjweaver-1.9.5.jar, file:/D:/repository/com/google/guava/guava/28.2-android/guava-28.2-android.jar, file:/D:/repository/com/google/guava/failureaccess/1.0.1/failureaccess-1.0.1.jar, file:/D:/repository/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar, file:/D:/repository/org/checkerframework/checker-compat-qual/2.5.5/checker-compat-qual-2.5.5.jar, file:/D:/repository/com/google/errorprone/error_prone_annotations/2.3.4/error_prone_annotations-2.3.4.jar, file:/D:/repository/com/google/j2objc/j2objc-annotations/1.3/j2objc-annotations-1.3.jar, file:/D:/repository/io/reactivex/rxjava-reactive-streams/1.2.1/rxjava-reactive-streams-1.2.1.jar, file:/D:/repository/org/reactivestreams/reactive-streams/1.0.3/reactive-streams-1.0.3.jar, file:/D:/repository/org/springframework/boot/spring-boot-starter-actuator/2.2.5.RELEASE/spring-boot-starter-actuator-2.2.5.RELEASE.jar, file:/D:/repository/org/springframework/boot/spring-boot-actuator-autoconfigure/2.2.5.RELEASE/spring-boot-actuator-autoconfigure-2.2.5.RELEASE.jar, file:/D:/repository/org/springframework/boot/spring-boot-actuator/2.2.5.RELEASE/spring-boot-actuator-2.2.5.RELEASE.jar, file:/D:/repository/io/micrometer/micrometer-core/1.3.5/micrometer-core-1.3.5.jar, file:/D:/repository/org/latencyutils/LatencyUtils/2.0.3/LatencyUtils-2.0.3.jar, file:/D:/repository/org/projectlombok/lombok/1.18.12/lombok-1.18.12.jar, file:/D:/repository/org/springframework/cloud/spring-cloud-context/2.2.3.RELEASE/spring-cloud-context-2.2.3.RELEASE.jar, file:/D:/repository/org/springframework/security/spring-security-crypto/5.2.2.RELEASE/spring-security-crypto-5.2.2.RELEASE.jar, file:/D:/repository/cn/hutool/hutool-all/5.3.5/hutool-all-5.3.5.jar, file:/D:/repository/de/codecentric/spring-boot-admin-starter-client/2.2.3/spring-boot-admin-starter-client-2.2.3.jar, file:/D:/repository/de/codecentric/spring-boot-admin-client/2.2.3/spring-boot-admin-client-2.2.3.jar, file:/F:/soft/idea/idea/lib/idea_rt.jar, file:/F:/soft/idea/idea/plugins/java/lib/rt/debugger-agent.jar] . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.2.5.RELEASE)17:22:13 [main] DEBUG c.c.f.a.s.b.ApolloApplicationContextInitializer - Apollo bootstrap config is not enabled for context org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@3e6534e7, started on Thu Jan 01 08:00:00 CST 1970, parent: org.springframework.context.annotation.AnnotationConfigApplicationContext@62f87c44, see property: ${apollo.bootstrap.enabled}17:22:13 [main] INFO com.nisbos.goods.GoodsApplication - No active profile set, falling back to default profiles: default。。。省略后续日志这个日志如果不细看还认为是启动报错,其实这段日志是springboot2的主动拆卸日志报告。平时咱们是不大须要,因而咱们能够敞开。敞开的办法也很简略,从控制台打印的日志信息咱们能够得悉主动拆卸日志报告是由 ...

July 15, 2020 · 9 min · jiezi

初探Logback学会看懂Logback配置文件

前言在现如今的应用中,日志已经成为了一个非常重要的工具。通过系统打印的日志,可以监测系统的运行情况,排查系统错误的原因。日志从最早期的System.out.print到如今各种成熟的框架,使得日志打印更加规范化和清晰化。尤其是SLF4J的出现,为日志框架定义了通用的FACADE接口和能力。只需要在应用中引入SLF4J包和具体实现该FACADE的日志包,上层应用就可以只需要面向SLF4J接口编程,而无需关心具体的底层的日志框架,实现了上层应用和底层日志框架的解耦。Logback作为一个支持SLF4J通用能力的框架,成为了炙手可热的日志框架之一。今天就来稍微了解一下Logback日志的一些基础能力以及配置文件。 快速上手Logback引入MAVEN依赖logback主要由三个模块组成,分别是logback-core,logback-classic和logback-access。其中logback-core是整个Logback的核型模块,logback-classic支持了SLF4J FACADE,而logback-access则集成了Servlet容齐来提供HTTP日志功能,适用于web应用。下面主要是基于logback-classic来进行介绍。 引入logback-classic的包如下: <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.3.0-alpha5</version></dependency>上面拉取的Maven包基于传递性远离,会自动拉取logback-classic,logback-core和slf4j-api.jar,因此无需在项目中再额外声明SLF4J和logback-core的依赖。 使用Logback因为logback-classic实现了SLF4J FACADE,所以上层应用只需要面向SLF4J的调用语法即可。下面代码展示了如何获取到Logger对象用来打印日志。 import org.slf4j.Logger;import org.slf4j.LoggerFactory;import ch.qos.logback.classic.LoggerContext;import ch.qos.logback.core.util.StatusPrinter;public class HelloWorld2 { public static void main(String[] args) { //这里的Logger和LoggerFactory均为SLF4J的类,真正调用时会使用Logback的日志能力 //getLogger方法中传入的是Logger的名称,这个名称在后面讲解配置文件中的<logger>时会继续提到 Logger logger = LoggerFactory.getLogger("chapters.introduction.HelloWorld2"); //打印一条Debug级别的日志 logger.debug("Hello world."); //获取根Logger,使用场景比较少 Logger rootLogger = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME); }}日志级别Logback中每一个Logger都有对应的日志级别,该日志级别可以是Logger自己定义的,也可以是从父Logger上继承下来的。Logback一共支持5个日志级别,从高到低分别是ERROR,WARN,INFO,DEBUG,TRACE。Logger的日志级别决定了哪些级别的日志可以被输出。只有大于等于该Logger级别的日志才会被打印出来。比如假设上文中获取的名为"chapters.introduction.HelloWorld2"的Logger日志级别为INFO,则调用logger.debug("xxx")不会输出日志内容,因为DEBUG日志级别低于INFO日志级别。 日志级别可以帮助我们控制日志打印的粒度,比如在开发环境可以将日志级别设置到DEBUG帮助排查问题,而在生产环境则可以将日志级别设置到INFO,从而减少不必要的打印日志带来的性能影响。 参数化输出有时候我们往往并不只是打印出一条完整的日志,而是希望在日志中附带一些运行中参数,如下: Logger logger = LoggerFactory.getLogger("chapters.introduction.HelloWorld2");logger.debug("Hello World To " + username);上文的日志中除了打印了一些结构化的语句,还拼接了运行时执行这段逻辑的用户的名称。这里就会带来一个问题,即字符串拼接的问题。虽然JVM对String字符串的拼接已经进行了优化,但是假如当前的日志级别为INFO,那么这段代码所执行字符串拼接操作就是完全不必要的。因此,建议在代码加上一行日志级别的判断进行优化,如下: //非debug级别不会执行字符串拼接操作,但是debug级别会执行两次isDebugEnabled操作,性能影响不大if(logger.isDebugEnabled()) { logger.debug("Hello World To " + username);}但是,logback并不推荐在系统中使用字符串拼接的方式来输出日志,而是提倡使用参数传递的方式,由logback自己来执行日志的序列化。如下: //logger方法会判断是否为debug级别,再决定将entry序列化拼接如字符串logger.debug("The entry is {}.", entry);这种日志输出方式就无需额外包一层日志级别的判断,因为logger.debug方法内部自己会判断一次日志级别,再去执行日志内容转码的操作。注意,传入的参数必须实现了toString方法,不然日志在对对象进行转码时,只会打印出对象的内存地址,而不是对象中的具体内容 整体架构前文已经简单介绍了logback包含的三个主要模块,以及如何在代码中基于SLF4J FACADE自由的使用日志框架。下面开始从配置文件的角度来了解如何配置Logback。Logback主要支持XML和groovy结构的配置文件,下文中将以XML结构为基础进行介绍。 上图为官网中对Logback配置文件整体结构的描述。配置文件以<configuration>作为根元素,其下包含1个<root>元素用于定义根日志的配置信息,还有0到多个<logger>元素以及0到多个<appender>元素。其中<logger>元素对应了应用中通过LoggerFactory.getLogger()获取到的日志工具,<appender>元素定义了日志的输出目的地,一个<logger>可以关联多个<appender>,即允许将同样的一行日志输出到多个目的地。 ...

November 3, 2019 · 1 min · jiezi

Spring-Boot-2-集成log4j2日志框架

前言Log4j2是 Log4j 的进化版本,并提供了许多 Logback 可用的改进,同时解决了 Logback 体系结构中的一些固有问题。而且日志处理中我们会用到kafka作为日志管道。而kafka客户端依赖与Logback的兼容不是很完美,你可以选择排除依赖冲突或者使用Log4j2 。<!-- more --> 排除Logback依赖Spring Boot 2.x默认使用Logback日志框架,要使用 Log4j2必须先排除 Logback。 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <exclusions> <!--排除logback--> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions></dependency>引入Log4j2依赖<!--log4j2 依赖--><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId></dependency>上面的 log4j2 已经适配了slf4j日志门面,所以我们的代码无需替换,只需要替换具体的日志框架以及对应的配置文件。 配置Log4j2创建log4j2.xml文件,放在工程resources目录里。这样就可以不加任何配置。如果你需要指定配置文件需要在Spring boot 配置文件application.yml中指定 logging.config 属性。下面是一份比较详细的 log4j2 配置文件 : <?xml version="1.0" encoding="UTF-8"?> <!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL --> <!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出--> <!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数--> <configuration status="WARN" monitorInterval="30"> <!--先定义所有的appender--> <appenders> <!--这个输出控制台的配置--> <console name="Console" target="SYSTEM_OUT"> <!--输出日志的格式--> <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> </console> <!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,这个也挺有用的,适合临时测试用--> <File name="log" fileName="log/test.log" append="false"> <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/> </File> <!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档--> <RollingFile name="RollingFileInfo" fileName="${sys:user.home}/logs/info.log" filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log"> <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--> <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/> <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> <Policies> <TimeBasedTriggeringPolicy/> <SizeBasedTriggeringPolicy size="100 MB"/> </Policies> </RollingFile> <RollingFile name="RollingFileWarn" fileName="${sys:user.home}/logs/warn.log" filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log"> <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/> <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> <Policies> <TimeBasedTriggeringPolicy/> <SizeBasedTriggeringPolicy size="100 MB"/> </Policies> <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,这里设置了20 --> <DefaultRolloverStrategy max="20"/> </RollingFile> <RollingFile name="RollingFileError" fileName="${sys:user.home}/logs/error.log" filePattern="${sys:user.home}/logs/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log"> <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/> <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> <Policies> <TimeBasedTriggeringPolicy/> <SizeBasedTriggeringPolicy size="100 MB"/> </Policies> </RollingFile> </appenders> <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效--> <loggers> <!--过滤掉spring和mybatis的一些无用的DEBUG信息--> <logger name="org.springframework" level="INFO"/> <logger name="org.mybatis" level="INFO"/> <root level="all"> <appender-ref ref="Console"/> <appender-ref ref="RollingFileInfo"/> <appender-ref ref="RollingFileWarn"/> <appender-ref ref="RollingFileError"/> </root> </loggers> </configuration>基本上你拿上面的配置根据你自己的需要更改一下即可生效。 windows 下 ${sys:user.home} 会将日志打印到用户目录下 ...

October 9, 2019 · 2 min · jiezi

通过Arthas来动态打印日志

需求线上一个Spring boot应用 通过MyBatis来操作数据库 定位一个线上问题 想动态打印sql日志 解决通过Arthas工具来实现上述的需求 $ ognl '@org.slf4j.LoggerFactory@getLogger("com.foo.cpts.mapper")'@Logger[ serialVersionUID=@Long[5454405123156820674], FQCN=@String[ch.qos.logback.classic.Logger], name=@String[com.foo.cpts.mapper], level=null, effectiveLevelInt=@Integer[20000], parent=@Logger[Logger[com.foo.cpts]], childrenList=@CopyOnWriteArrayList[isEmpty=false;size=2], aai=null, additive=@Boolean[true], loggerContext=@LoggerContext[ch.qos.logback.classic.LoggerContext[default]],]刚开始Mapper包对应的日志级别是null 将其改成DEBUG $ ognl '@org.slf4j.LoggerFactory@getLogger("com.foo.cpts.mapper").setLevel(@ch.qos.logback.classic.Level@DEBUG)'null$ ognl '@org.slf4j.LoggerFactory@getLogger("com.foo.cpts.mapper")'@Logger[ serialVersionUID=@Long[5454405123156820674], FQCN=@String[ch.qos.logback.classic.Logger], name=@String[com.foo.cpts.mapper], level=@Level[DEBUG], effectiveLevelInt=@Integer[10000], parent=@Logger[Logger[com.foo.cpts]], childrenList=@CopyOnWriteArrayList[isEmpty=false;size=2], aai=null, additive=@Boolean[true], loggerContext=@LoggerContext[ch.qos.logback.classic.LoggerContext[default]],]此时当调用Mapper方法时 能打印出sql日志了 同样关闭sql日志 改成info即可 $ ognl '@org.slf4j.LoggerFactory@getLogger("com.foo.cpts.mapper").setLevel(@ch.qos.logback.classic.Level@INFO)'null补充因为打印sql日志对应的配置是 logging.level.com.foo.cpts.mapper=DEBUG刚开始想着通过下面的方式来实现 ognl '@java.lang.System@setProperty("logging.level.com.foo.cpts.mapper","DEBUG")'但是该方式并未起作用

June 26, 2019 · 1 min · jiezi

从0手写springCloud项目二-框架代码详解

写在前面前面一篇将springCloud所需的几个组件搭建起来了,接下来以user模块为例子主要记录一下项目中集成的技术,框架,和使用方式。我想从以下几个地方总结: mybatis-plus3lcn5.0.2liquibaseoauth2others(es,rabbitmq,全局异常处理,feign之间异常处理,logback,mysql,redis)集成mybatis-plus3可能有些同学常用的持久层框架是jpa,但是就我实践而言,mybatisplus好用的不是一丁点,个人建议用mybatisplus...现在plus3的版本支持的还是蛮多的:乐观锁,版本号,代码生成器,分页插件,热加载,通用枚举,自动填充,动态数据源....详见官网(https://mp.baomidou.com),而且3.0集成也比2.x简单了不少,最简单的只需要加一个pom坐标即可,但是如果需要个性化配置,我们还是要写config,当然也不麻烦,很简单的。 pom <!--mybatis-plus--><dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.1.0</version></dependency>yml //可以看到我的各个文件存放的路径分别在哪里 mybatis: type-aliases-package: cn.iamcrawler.crawler_common.domain.goddess mapper-locations: cn/iamcrawler/crawlergoddess/mapper/*Mapper.xml type-handlers-package: cn.iamcrawler.crawlergoddess.mapper.typehandler global-config: refresh-mapper: true 那么接下来继承plus的两个方法就可以了 /** * Created by liuliang on 2019/3/21. */public interface DataUserMapper extends BaseMapper<DataUser> {}@Service@Slf4jpublic class DataUserService extends ServiceImpl<DataUserMapper,DataUser>{}ps:大家可以想一想,spring的controller,service,mapper需要使用注解将其标识为bean才能扫描得到,而这里的mapper却没有添加@Repository注解,这是因为我在注解类上加了一个注解,指定spring启动的时候扫描到这个包下,所以这里就可以不用添加@Repository注解,也可以被spring扫描到啦。主类的注解如下: @SpringBootApplication@EnableEurekaClient@EnableFeignClients@EnableTransactionManagerServer@MapperScan("cn.iamcrawler.crawlergoddess.mapper")//上面说的,就是这个注解!public class CrawlerGoddessApplication { public static void main(String[] args) { SpringApplication.run(CrawlerGoddessApplication.class, args); }}是的,就是这么简单。plus最简单的集成就完成了,接下来继承mapper接口,service接口就可以写sql了。具体详见mybatis-plus官网。 ps:最后还有一个小知识点,其实大家可以看到,我的xml是放在和mapper一起的(默认是放在resource下面,打包成jar的时候路径就是我们常说的classpath),而我们知道,在maven打包jar的时候,是不会扫描这个java下的.xml文件的,为什么我打包jar也可以扫描呢?是因为我在maven里面配置了一个将java下的xml也打包的程序,如下: <build> <finalName>crawler-goddess</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> <!---我上面说的就是下面这个配置哦--> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> <resource> <directory>src/main/resources</directory> </resource> </resources> </build>集成lcn5.0.2微服务集成分布式事务框架已经不是一个新鲜的事情,在这方面,我前面也写过好几个文章介绍过,其中有springboot1.5.9+lcn4.1.0( https://segmentfault.com/a/11... ), 有springboot2.1.3+lcn5.0.2( https://segmentfault.com/a/11... )。大家可以按需阅读并配置,当然这里我这个demo配置的是5.0.2版本.毕竟这个版本比4.1.0强大了不少,并且配置也更加方便。具体大家也可以参照lcn官网,描述的非常详细(http://www.txlcn.org) ...

June 18, 2019 · 1 min · jiezi

spring-boot下使用LogBack,使用HTTP协议将日志推送到日志服务器(二)

上文中,我们实现了将LogBack的日志信息实时的推送到日志服务器的功能。但实时进行推送,必然会增加日志服务器的压力。本文将阐述另一种定时推送的方法,以减轻日志服务器的压力。目标:每10秒钟统一推送这期间产生的日志示例代码地址https://github.com/mengyunzhi/springBootSampleCode/tree/master/log-back 开发环境: java1.8 + spring-boot:2.1.2定义application.properties为了与官方文档更加统一,在此,我们如下定义application.properties# URLlogback.loggly.endpointUrl=http://localhost:8081/log配置logback-spring.xml<?xml version=“1.0” encoding=“UTF-8”?><!–启用debug模式后,将在spring-boot 大LOG上方打印中logBack的配置信息–><configuration debug=“true” scan=“false” scanPeriod=“30 seconds”> <!–引入application配置信息–> <springProperty scope=“context” name=“logback.loggly.endpointUrl” source=“logback.loggly.endpointUrl” defaultValue=“localhost”/> <!–包含配置文件 org/springframework/boot/logging/logback/defaults.xml–> <include resource=“org/springframework/boot/logging/logback/defaults.xml”/> <!–定义变量LOG_FILE,值为${LO…}–> <property name=“LOG_FILE” value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}}/spring.log}"/> <!–包含配置文件,该配置文件中,定义了 控制台日志是按什么规则,什么形式输出的–> <include resource=“org/springframework/boot/logging/logback/console-appender.xml”/> <!–包含配置文件,该配置文件中,定义了 文件日志是按什么规则,什么形式输出的–> <include resource=“org/springframework/boot/logging/logback/file-appender.xml”/> <!–引入第三方appender, 起名为http–> <appender name=“HTTP” class=“ch.qos.logback.ext.loggly.LogglyAppender”> <!–请求的地址–> <endpointUrl>${logback.loggly.endpointUrl}</endpointUrl> <!–定义过滤器–> <filter class=“com.mengyunzhi.sample.logback.Filter”/> <!–定义输出格式JSON–> <layout class=“ch.qos.logback.contrib.json.classic.JsonLayout”> <jsonFormatter class=“ch.qos.logback.contrib.jackson.JacksonJsonFormatter”> <prettyPrint>true</prettyPrint> </jsonFormatter> <timestampFormat>yyyy-MM-dd’ ‘HH:mm:ss.SSS</timestampFormat> </layout> </appender> <!–引用第三方appender, 起名为batchHttp–> <appender name=“batchHttp” class=“ch.qos.logback.ext.loggly.LogglyBatchAppender”> <endpointUrl>${logback.loggly.endpointUrl}</endpointUrl> <flushIntervalInSeconds>10</flushIntervalInSeconds> <!–定义输出格式JSON–> <layout class=“ch.qos.logback.contrib.json.classic.JsonLayout”> <jsonFormatter class=“ch.qos.logback.contrib.jackson.JacksonJsonFormatter”> <prettyPrint>true</prettyPrint> </jsonFormatter> </layout> </appender> <!–定义日志等级–> <root level=“INFO”> <!–启用第一个appender为CONSOLE, 该名称定义于org/springframework/boot/logging/logback/console-appender.xml中–> <appender-ref ref=“CONSOLE”/> <!–启用第二个appender为FILE, 该名称定义于org/springframework/boot/logging/logback/file-appender.xml中–> <appender-ref ref=“FILE”/> <!–启用第三个appender为HTTP–> <!–<appender-ref ref=“HTTP”/>–> <!–启用第4个appender为batchHttp–> <appender-ref ref=“batchHttp”/> </root></configuration>主要变更信息如下:为了更好的和官方同步,将变量名称由logUrl变更为:logback.loggly.endpointUrl引用了新的变量信息: logback.loggly.endpointUrl加了新的appender -> batchHttp启动测试启动service子模块,启动本项目。打开浏览器,并访问:http://localhost:8080/send发送方日志:2019-03-25 15:29:02.093 INFO 28934 — [nio-8080-exec-6] c.m.sample.logback.LogBackApplication : info2019-03-25 15:29:02.093 WARN 28934 — [nio-8080-exec-6] c.m.sample.logback.LogBackApplication : warn2019-03-25 15:29:02.093 ERROR 28934 — [nio-8080-exec-6] c.m.sample.logback.LogBackApplication : error2019-03-25 15:29:11.564 INFO 28934 — [nio-8080-exec-8] c.m.sample.logback.LogBackApplication : info2019-03-25 15:29:11.565 WARN 28934 — [nio-8080-exec-8] c.m.sample.logback.LogBackApplication : warn2019-03-25 15:29:11.565 ERROR 28934 — [nio-8080-exec-8] c.m.sample.logback.LogBackApplication : error挡收方日志2019-03-25 15:29:08.286 INFO 28428 — [nio-8081-exec-9] c.m.s.l.service.ServiceApplication : { “timestamp” : “1553498942093”, “level” : “INFO”, “thread” : “http-nio-8080-exec-6”, “logger” : “com.mengyunzhi.sample.logback.LogBackApplication”, “message” : “info”, “context” : “default”}{ “timestamp” : “1553498942093”, “level” : “WARN”, “thread” : “http-nio-8080-exec-6”, “logger” : “com.mengyunzhi.sample.logback.LogBackApplication”, “message” : “warn”, “context” : “default”}{ “timestamp” : “1553498942093”, “level” : “ERROR”, “thread” : “http-nio-8080-exec-6”, “logger” : “com.mengyunzhi.sample.logback.LogBackApplication”, “message” : “error”, “context” : “default”}2019-03-25 15:29:18.289 INFO 28428 — [nio-8081-exec-1] c.m.s.l.service.ServiceApplication : { “timestamp” : “1553498951564”, “level” : “INFO”, “thread” : “http-nio-8080-exec-8”, “logger” : “com.mengyunzhi.sample.logback.LogBackApplication”, “message” : “info”, “context” : “default”}{ “timestamp” : “1553498951565”, “level” : “WARN”, “thread” : “http-nio-8080-exec-8”, “logger” : “com.mengyunzhi.sample.logback.LogBackApplication”, “message” : “warn”, “context” : “default”}{ “timestamp” : “1553498951565”, “level” : “ERROR”, “thread” : “http-nio-8080-exec-8”, “logger” : “com.mengyunzhi.sample.logback.LogBackApplication”, “message” : “error”, “context” : “default”}我们发现:发送方于29分02秒时,生成了3条日志;接收方于29分08秒接收到了1条日志,该日志包括了上面3条日志信息。发送方于29分11秒时,生成了3条日志;接收方于29分18秒(距离上次接收的间隔为10秒)接收到了第2条日志,该日志包括了29分11秒时生成的3条日志信息。总结对于小白级别的我们而言,我们想到的,别人其它早就实现了。所以大多数的我们,最终比接是DEBUG的能力、看文档的能力、对软件工程理解的能力、对业务的掌控能力。而编码能力,则是基本功,没有它不行,只有它也不行。参考文档:https://github.com/qos-ch/logback-extensions/wiki/Loggly ...

March 25, 2019 · 2 min · jiezi

window环境下搭建简单ELK日志收集

window环境下搭建简单ELK日志收集前言本文主要介绍如何在window环境下部署elk日志收集,网络上大部分是linux的,刚好这边服务需要用到window 环境,配置方式有点不同,大体是一样的。部署环境window server 2008 R2依赖第三方组件JDK8+elasticsearch-5.6.14logstash-5.6.14kibana-5.6.14-windows-x86redis-3.2下载地址官网: https://www.elastic.co/简单的流程示意图redis跟elasticsearch 都支持集群部署,本文只简单描述如何单独部署部署步骤elk三件套版本最好一致,elasticsearch小版本可以高,但是不可以低。关于elasticsearch 的搭建和配置可以参考之前的一篇文章DUBBO监控环境搭建启动redisredis默认只允许本地访问,如果要redis可以远程需要修改redis.windows.conf将 bind 127.0.0.1 注释掉,在redis3.2版本以上增加了一个保护模式,所以还需要修改protected-mode no#bind 127.0.0.1# Protected mode is a layer of security protection, in order to avoid that# Redis instances left open on the internet are accessed and exploited.## When protected mode is on and if:## 1) The server is not binding explicitly to a set of addresses using the# “bind” directive.# 2) No password is configured.# By default protected mode is enabled. You should disable it only if# you are sure you want clients from other hosts to connect to Redis# even if no authentication is configured, nor a specific set of interfaces# are explicitly listed using the “bind” directive.protected-mode no配置logstashlogstash的作用是将redis中的日志信息收集并保存到elasticsearch中,redis只是作为缓存保存数据,logstash取完之后会删除redis中的数据信息。1.指定启动JDK指定JDK信息,如果有多个JDK需要指定,如果系统默认1.8可以跳过在 logstash-5.6.14bin的setup.bat 中加入这句set JAVA_HOME=C:Program FilesJavajdk1.8.0_181set JAVA_HOME=C:\Program Files\Java\jdk1.8.0_181rem ### 1: determine logstash homerem to do this, we strip from the path until werem find bin, and then strip bin (there is an assumption here that there is norem nested directory under bin also named bin)2.配置输入输出源在 logstash-5.6.14bin中新建 logback.config(名字任意) 配置文件,定义输入输出input接受数据的输入,这边指定到redis;output输出文件到指定的地方, elasticsearch;这边的index索引会在kibana那边配置用到这个输入和输出的配置支持的类型比较多,有兴趣专研的可以参考官网配置logstash输入配置logstash输出配置另外logstash还支持filter过滤器设置input { redis { data_type => “list” key => “logstash” host => “127.0.0.1” port => 6379 threads => 5 codec => “json” }}filter {}output { elasticsearch { hosts => [“127.0.0.1:9200”] index => “logstash-%{type}-%{+YYYY.MM.dd}” document_type => “%{type}” workers => 1 flush_size => 20 idle_flush_time => 1 template_overwrite => true } stdout{}}启动logstash配置kibana找到kibana-5.6.14-windows-x86config 中kibana.yml,1.配置host 0.0.0.0,运行外网访问,默认只能本地访问 2.配置es的地址。server.host: “0.0.0.0”# Enables you to specify a path to mount Kibana at if you are running behind a proxy. This only affects# the URLs generated by Kibana, your proxy is expected to remove the basePath value before forwarding requests# to Kibana. This setting cannot end in a slash.#server.basePath: “”# The maximum payload size in bytes for incoming server requests.#server.maxPayloadBytes: 1048576# The Kibana server’s name. This is used for display purposes.#server.name: “your-hostname”# The URL of the Elasticsearch instance to use for all your queries.elasticsearch.url: “http://localhost:9200"启动Kibana.bat跟logback的结合添加maven支持,引入logback-redis-appender.jar包 <dependency> <groupId>com.cwbase</groupId> <artifactId>logback-redis-appender</artifactId> <version>1.1.3</version> <exclusions> <exclusion> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </exclusion> </exclusions> </dependency>配置logback.xml,这里的key需要跟上面配置的logback.config配置文件中配置的key相匹配 <appender name=“LOGSTASH” class=“com.cwbase.logback.RedisAppender”> <source>demo</source> <type>dubbo</type> <host>127.0.0.1</host> <key>logstash</key> <tags>dev</tags> <mdc>true</mdc> <location>true</location> <callerStackIndex>0</callerStackIndex> </appender> <!– 语句监控 end –> <root level=“INFO”> <appender-ref ref=“stdout” /> <appender-ref ref=“file-stdout” /> <appender-ref ref=“file-error” /> <appender-ref ref=“LOGSTASH” /> </root>启动服务工程,logback会将日志发送到redis,打开kibana的页面,第一次打开会提示创建索引规则,就是logback.config中es配置的索引logstash-,创建好之后就可以看到日志已经被采集到elasticsearch中,我们在页面就可以查看日志信息。kibana默认端口http://127.0.0.1:5601结尾最后说明下关于清除日志,ElasticSearch 5.x版本中删除过期ttl属性,所以我们需要定时清理es的索引,在服务中建一个bat定时执行,以下为删除30天前的全部索引。set DATA=date -d "-30 days" +%Y.%m.%dcurl -XDELETE http://127.0.0.1:9200/-${DATA}参考文档http://www.cnblogs.com/ASPNET…https://blog.csdn.net/xuezhan… ...

January 22, 2019 · 2 min · jiezi

spring-boot下使用LogBack,使用HTTP协议将日志推送到日志服务器

当项目上线发生错误或是异常后,我们总是期望能够在第一时间内收到用户的详细反馈。当然,这也无疑会是一个非常好的提升软件质量的方法。但如果用户不愿意反馈呢?此时,我们便可以借助日志系统,比如:每隔一小时,服务器自动向我们报告一下当前的服务情况。当有错误或是警告或是异常信息时,及时向我们的报告等。在基于上述的需求上,我们结合spring-boot内置的LogBack,来给出将warn,error信息发送到远程服务器的示例。项目地址https://github.com/mengyunzhi/sample/tree/master/spring-boot/log-back开发环境: java1.8 + spring-boot:2.1.2实现步骤引入相关的依赖pom.xml<?xml version=“1.0” encoding=“UTF-8”?><project xmlns=“http://maven.apache.org/POM/4.0.0" xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=“http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.2.RELEASE</version> <relativePath/> <!– lookup parent from repository –> </parent> <groupId>com.mengyunzhi.sample</groupId> <artifactId>log-back</artifactId> <version>0.0.1-SNAPSHOT</version> <name>log-back</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>启动项目控制台打印信息如下: . ____ _ __ _ _ /\ / ’ __ _ () __ __ _ \ \ \ ( ( )__ | ‘_ | ‘| | ‘ / | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.1.2.RELEASE)2019-01-16 10:35:04.999 INFO 1571 --- [ main] c.m.sample.logback.LogBackApplication : Starting LogBackApplication on panjiedeMac-Pro.local with PID 1571 (/Users/panjie/github/mengyunzhi/sample/spring-boot/log-back/target/classes started by panjie in /Users/panjie/github/mengyunzhi/sample/spring-boot/log-back)2019-01-16 10:35:05.002 INFO 1571 --- [ main] c.m.sample.logback.LogBackApplication : No active profile set, falling back to default profiles: default2019-01-16 10:35:05.913 INFO 1571 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)2019-01-16 10:35:05.934 INFO 1571 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]2019-01-16 10:35:05.935 INFO 1571 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.14]2019-01-16 10:35:05.940 INFO 1571 --- [ main] o.a.catalina.core.AprLifecycleListener : The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [/Users/panjie/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.]2019-01-16 10:35:06.008 INFO 1571 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext2019-01-16 10:35:06.008 INFO 1571 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 968 ms2019-01-16 10:35:06.183 INFO 1571 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'2019-01-16 10:35:06.335 INFO 1571 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''2019-01-16 10:35:06.338 INFO 1571 --- [ main] c.m.sample.logback.LogBackApplication : Started LogBackApplication in 1.616 seconds (JVM running for 2.093)配置logback新建resources/logback-spring.xml,初始化以下信息:&lt;?xml version="1.0" encoding="UTF-8"?&gt;&lt;!--开启debug模式--&gt;&lt;configuration debug="true"&gt;&lt;/configuration&gt;启动项目,控制台打印信息如下:10:33:41,053 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - End of configuration.10:33:41,054 |-INFO in org.springframework.boot.logging.logback.SpringBootJoranConfigurator@55a1c291 - Registering current configuration as safe fallback point10:33:41,067 |-WARN in Logger[org.springframework.boot.context.logging.ClasspathLoggingApplicationListener] - No appenders present in context [default] for logger [org.springframework.boot.context.logging.ClasspathLoggingApplicationListener]. . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \( ( )\___ | '_ | '_| | '_ \/ _ | \ \ \ \ \/ )| |)| | | | | || (| | ) ) ) ) ’ || .__|| ||| |_, | / / / / =========||==============|/=//// :: Spring Boot :: (v2.1.2.RELEASE)如何判断配置成功了?我们比较上面两个日志,第一个是没有配置logback-spring.xml,第二个是配置logback-spring.xml了。是的,如果我们发现spring 大LOG打印前,在控制台中打印了ch.qos…输出的日志信息,则说明logback-spring.xml。同时,如果logback-spring.xml起作用的话,我们还发现spring 大LOG下面,一行日志也没有了。是的,由于logback-spring.xml对日志输出进行了控制,而配置信息中,我们又没有写任何的信息,为空。所以spring 大LOG后面当然就不显示任何日志了信息了。查看spring-boot的默认配置我们使用IDEA的打开文件快捷键commod+shift+o,输入base.xml,然后再使用查看文件位置快捷键option+F1来查看文件位置。更来到了spring-boot的默认配置。上述文件,即为spring-boot的默认配置。下面,我们将以上配置引入到我们的logback-spring.xml中,来实现spring-boot的默认日志效果。实现默认效果复制相应的代码至logback-spring.xml中:<?xml version=“1.0” encoding=“UTF-8”?><!–启用debug模式后,将在spring-boot 大LOG上方打印中logBack的配置信息–><configuration debug=“true”> <!–包含配置文件 org/springframework/boot/logging/logback/defaults.xml–> <include resource=“org/springframework/boot/logging/logback/defaults.xml” /> <!–定义变量LOG_FILE,值为${LO…}–> <property name=“LOG_FILE” value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}}/spring.log}”/> <!–包含配置文件,该配置文件中,定义了 控制台日志是按什么规则,什么形式输出的–> <include resource=“org/springframework/boot/logging/logback/console-appender.xml” /> <!–包含配置文件,该配置文件中,定义了 文件日志是按什么规则,什么形式输出的–> <include resource=“org/springframework/boot/logging/logback/file-appender.xml” /> <!–定义日志等级–> <root level=“INFO”> <!–启用第一个appender为CONSOLE, 该名称定义于org/springframework/boot/logging/logback/console-appender.xml中–> <appender-ref ref=“CONSOLE” /> <!–启用第二个appender为FILE, 该名称定义于org/springframework/boot/logging/logback/file-appender.xml中–> <appender-ref ref=“FILE” /> </root></configuration>然后我们再次启动项目,会发现与原spring-boot相比较,在spring 大LOGO前多一些日志相关的配置信息输出,其它的信息是一致的。实现http日志appenderappender通过上面的注释,我们猜测:appender这个东西,能够把日志处理成我们想要的样子。在进行官方文档的学习中,我们发现了很多已经存在的appender。与我们的需求比较相近的是SyslogAppender。liunx有标准的syslog服务,用于接收syslog日志。通过查询相关资料,我们获悉,此syslog服务,一身作用于514端口上。直接使用UDP或TCP协议发送MESSAGE。而我们此时想用更熟悉的http协议。所以暂时放弃。小于1024的均为已知端口,可以通过端口号来查询对应的协议或服务名称。第三方http appender除了按官方的教程来写自己的http appender,还有一些比较好的第三方appender可以使用,比如:LogglyAppender。找到官方文档,并引入:pom.xml <dependency> <groupId>org.logback-extensions</groupId> <artifactId>logback-ext-loggly</artifactId> <version>0.1.5</version> </dependency>logback-spring.xml<?xml version=“1.0” encoding=“UTF-8”?><!–启用debug模式后,将在spring-boot 大LOG上方打印中logBack的配置信息–><configuration debug=“true”> <!–包含配置文件 org/springframework/boot/logging/logback/defaults.xml–> <include resource=“org/springframework/boot/logging/logback/defaults.xml” /> <!–定义变量LOG_FILE,值为${LO…}–> <property name=“LOG_FILE” value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}}/spring.log}”/> <!–包含配置文件,该配置文件中,定义了 控制台日志是按什么规则,什么形式输出的–> <include resource=“org/springframework/boot/logging/logback/console-appender.xml” /> <!–包含配置文件,该配置文件中,定义了 文件日志是按什么规则,什么形式输出的–> <include resource=“org/springframework/boot/logging/logback/file-appender.xml” /> <!–引入第三方appender, 起名为http–> <appender name=“HTTP” class=“ch.qos.logback.ext.loggly.LogglyAppender”> <!–请求的地址–> <endpointUrl>http://localhost:8081/log</endpointUrl> </appender> <!–定义日志等级–> <root level=“INFO”> <!–启用第一个appender为CONSOLE, 该名称定义于org/springframework/boot/logging/logback/console-appender.xml中–> <appender-ref ref=“CONSOLE” /> <!–启用第二个appender为FILE, 该名称定义于org/springframework/boot/logging/logback/file-appender.xml中–> <appender-ref ref=“FILE” /> <!–启用第三个appender为HTTP–> <appender-ref ref=“HTTP” /> </root></configuration>测试测试方法如图:使用浏览器来访问当前项目的’/send’地址send中我们加入logger。再新建一个新项目,用来接收http appender发送过来的日志。建立测试方法LogBackApplication.javapackage com.mengyunzhi.sample.logback;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;@SpringBootApplication@RestControllerpublic class LogBackApplication { private static final Logger logger = LoggerFactory.getLogger(LogBackApplication.class); public static void main(String[] args) { SpringApplication.run(LogBackApplication.class, args); } @RequestMapping(“send”) public void send() { logger.info(“info”); logger.warn(“warn”); logger.error(“error”); }}接收模块新建一个spring boot项目,然后设置端口为8081。application.propertiesserver.port=8081ServiceApplication.javapackage com.mengyunzhi.sample.logback.service;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.http.HttpRequest;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;@SpringBootApplication@RestControllerpublic class ServiceApplication { private final static Logger logger = LoggerFactory.getLogger(ServiceApplication.class); public static void main(String[] args) { SpringApplication.run(ServiceApplication.class, args); } @RequestMapping(“log”) public void log(HttpServletRequest httpServletRequest) { logger.info(httpServletRequest.toString()); }}启动测试使用debug模式来启动两个项目,项目启动后,打开浏览器,输入:http://localhost:8080/send,并在8081端口上的接收位置打断点。查看断点信息:此时我们发现两项信息,也证明数据的确是发送和接收成功了:请求方法: POST请求的协议:http查看发送过来的MESSAGE @RequestMapping(“log”) public void log(HttpServletRequest httpServletRequest) throws IOException { logger.info(httpServletRequest.toString()); BufferedReader bufferedReader = httpServletRequest.getReader(); String str, wholeStr = “”; while((str = bufferedReader.readLine()) != null) { wholeStr += str; } logger.info(wholeStr); }如下:2019-01-16T06:06:49.707Z INFO [http-nio-8080-exec-1] org.apache.catalina.core.ContainerBase.[Tomcat].[localhost].[/]: Initializing Spring DispatcherServlet ‘dispatcherServlet’是的,正如你发现在的一样,一些本来打印在8081项目上面的info信息,被发送过来了。格式化数据传过来的字段串,并不友好,我们接下来将其进行格式化。格式化的方法有两种:1. 发送端格式化。2. 接收端格式化。接收端的格式化的思想是按空格将日志拆分,然后要传入到格式实体的不同的字段。这里不阐述,不实现。我们重点放在第1种,发送端使用第三方库进行格式化。pom.xml <!–log to json–> <dependency> <groupId>ch.qos.logback.contrib</groupId> <artifactId>logback-jackson</artifactId> <version>0.1.5</version> </dependency> <!–log to json–> <dependency> <groupId>ch.qos.logback.contrib</groupId> <artifactId>logback-json-classic</artifactId> <version>0.1.5</version> </dependency>logback.xml <!–引入第三方appender, 起名为http–> <appender name=“HTTP” class=“ch.qos.logback.ext.loggly.LogglyAppender”> <!–请求的地址–> <endpointUrl>http://localhost:8081/log</endpointUrl> <!–定义输出格式JSON–> <layout class=“ch.qos.logback.contrib.json.classic.JsonLayout”> <jsonFormatter class=“ch.qos.logback.contrib.jackson.JacksonJsonFormatter”> <prettyPrint>true</prettyPrint> </jsonFormatter> <timestampFormat>yyyy-MM-dd’ ‘HH:mm:ss.SSS</timestampFormat> </layout> </appender>再次启动项目,访问:http://localhost:8080/send查看断点。{ “timestamp” : “2019-01-16 14:17:54.783”, “level” : “ERROR”, “thread” : “http-nio-8080-exec-1”, “logger” : “com.mengyunzhi.sample.logback.LogBackApplication”, “message” : “error”, “context” : “default”}我们发现,以前的字段串,变成的json字符串,此时我们便可以在接收端建立对应的实体,来轻易的接收了。过滤掉INFO信息当前虽然实现了将日志写入到第三方HTTP日志服务器,但是一些我们不想写入的,比如说INFO信息,也被写入了。下面,我们写一个过滤器,来实现只输出warn有error的信息。新建过滤器Filter.javapackage com.mengyunzhi.sample.logback.service;import ch.qos.logback.classic.Level;import ch.qos.logback.classic.spi.LoggingEvent;import ch.qos.logback.core.filter.AbstractMatcherFilter;import ch.qos.logback.core.spi.FilterReply;import java.util.Arrays;import java.util.List;/** * @author panjie */public class Filter extends AbstractMatcherFilter { @Override public FilterReply decide(Object event) { if (!isStarted()) { return FilterReply.NEUTRAL; } LoggingEvent loggingEvent = (LoggingEvent) event; // 当级别为warn或error,时触发日志。 List<Level> eventsToKeep = Arrays.asList(Level.WARN, Level.ERROR); if (eventsToKeep.contains(loggingEvent.getLevel())) { return FilterReply.NEUTRAL; } else { return FilterReply.DENY; } }}设置过滤器:logback-spring.xml <!–引入第三方appender, 起名为http–> <appender name=“HTTP” class=“ch.qos.logback.ext.loggly.LogglyAppender”> <!–请求的地址–> <endpointUrl>http://localhost:8081/log</endpointUrl> <!–定义过滤器–> <filter class=“com.mengyunzhi.sample.logback.Filter”/> <!–定义输出格式JSON–> <layout class=“ch.qos.logback.contrib.json.classic.JsonLayout”> <jsonFormatter class=“ch.qos.logback.contrib.jackson.JacksonJsonFormatter”> <prettyPrint>true</prettyPrint> </jsonFormatter> <timestampFormat>yyyy-MM-dd’ ‘HH:mm:ss.SSS</timestampFormat> </layout> </appender>测试:只接收了warn与error的数据。统一配置我们在logback-spring.xml定义了<endpointUrl>http://localhost:8081/log</endpointUrl>,如何可以将此项配置搬迁到application.properties中呢?定义变量application.propertiesyunzhi.log.url=http://localhost:8081/log引用变量logback-spring.xml <!–引入application配置信息–> <springProperty scope=“context” name=“logUrl” source=“yunzhi.log.url” defaultValue=“localhost”/> <!–引入第三方appender, 起名为http–> <appender name=“HTTP” class=“ch.qos.logback.ext.loggly.LogglyAppender”> <!–请求的地址–> <endpointUrl>${logUrl}</endpointUrl>此时,我们便可以对logUrl在application.properties中进行统一管理了,当然了,不止如此,我们还可以在启动项目的时候,使用–yunzhi.log.url=xxx来轻松的改变日志接收地址。总结在整体实现的过程中,我们的解决思路仍然是:看官方文档,学官方文档,照抄官方文档。欲速则不达,有学习一门新的知识时,优先学习官方sample code,其次是官方文档。在学习的过程中,还要特别的注意版本号的问题;如何正确的快速的高效率测试的问题。TODO:每次有日志就进行一次请求,对网络资源是种浪费。将APPENDER修改为:每1分钟发送一次、每100条日志发送1次。 ...

January 16, 2019 · 4 min · jiezi

解读:spring-boot logging。记一次Logback在spring-boot中的使用方法

有个任务停留在任务列表中很久了:使用Appenders 完成 loger4j 的日志推送,始终没有成功实现。追其原因,仍然是官方的文档没有认真看。在spring-boot的项目中看到log4j,就想当然的认为Spring-boot使用的是log4j,然后不假思索的去google。最终导致的就是:功能没有实现,而且还浪费了很多不必要的时间,最后:还是老老实实的回来阅读spring-boot的官方文档。本文主要对官方文档Logging部分进行解读。原文地址:https://docs.spring.io/spring-boot/docs/current/reference/html/howto-logging.html.如果你使用的是不是最新版本,那么应该使用https://docs.spring.io/spring-boot/docs/版本号/reference/htmlsingle/#boot-features-logging 如:https://docs.spring.io/spring-boot/docs/1.5.3.RELEASE/reference/htmlsingle/#boot-features-logging76 日志在web开中,我们仅需要依赖于spring-boot-starter-web便自动启用了日志系统Logback。如果仅仅是想改变日志的等级,则可以直接使用logging.level前缀在application.properties中进行设置,比如:logging.level.org.springframework.web=DEBUGlogging.level.org.hibernate=ERROR除了控制日志的等级外,还可以使用logging.file来定义日志输入到的文件位置。如果我们还想配置更多选项,则可以在classpath(resourse)中定义logback.xml或logback-spring.xml。76.1 配置Logback找到logback.xml或logback-spring.xml,复制以下基本内容:<?xml version=“1.0” encoding=“UTF-8”?><configuration> <include resource=“org/springframework/boot/logging/logback/base.xml”/> <logger name=“org.springframework.web” level=“DEBUG”/></configuration>使用idea的ctrl+o来打开spring-boot jar中的base.xml,我们会看到配置信息包含一些特殊的字符,解读如下:${PID}当前的进程ID${LOG_FILE} 如果设置了logging.file,则使用logging.file做为日志输入文件。${LOG_PATH} 同上.指定日志输出路径。${LOG_EXCEPTION_CONVERSION_WORD} ..我们自己定义日志输入的方式和字符串时,当然也可以使用它们了。76.1.1 配置:将日志仅写入文件如果我们想禁用控制台的日志输出(生产环境中,我们的确是要这么做的),然后把日志写入某个日志文件的话。那么需要新建logback-spring.xml,并引入file-appender.xml,比如:<?xml version=“1.0” encoding=“UTF-8”?><configuration> <include resource=“org/springframework/boot/logging/logback/defaults.xml” /> <property name=“LOG_FILE” value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}/}spring.log}"/> <include resource=“org/springframework/boot/logging/logback/file-appender.xml” /> <root level=“INFO”> <appender-ref ref=“FILE” /> </root></configuration>然后:在application.properties定义logging.file来指定日志文件位置.例:logging.file=myapplication.log再看看上面是怎么回事:打开org/springframework/boot/logging/logback/file-appender.xml内容如下:<?xml version=“1.0” encoding=“UTF-8”?><!–File appender logback configuration provided for import, equivalent to the programmaticinitialization performed by Boot–><included> <appender name=“FILE” class=“ch.qos.logback.core.rolling.RollingFileAppender”> <encoder> <pattern>${FILE_LOG_PATTERN}</pattern> </encoder> <file>${LOG_FILE}</file> <rollingPolicy class=“ch.qos.logback.core.rolling.FixedWindowRollingPolicy”> <fileNamePattern>${LOG_FILE}.%i</fileNamePattern> </rollingPolicy> <triggeringPolicy class=“ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy”> <MaxFileSize>10MB</MaxFileSize> </triggeringPolicy> </appender></included>注意:这里面有个<appender name=“FILE”,指定了appender名称为FILE,对应logback-spring.xml的以下语句: <root level=“INFO”> <!–日志等级–> <appender-ref ref=“FILE” /> <!–指定appender为FILE,则前面我们刚刚找到的name值–> </root>总结有了以上内容,我们知道了如下知识点:spring-boot默认使用的是Logback而非log4j。我们可以单独建立logback-spring.xml来细化Logback的配置。在Logback中,是可以指定使用不同的appender来定义日志的输出的。是否可以自定义appender来达到将日志输出到我们的日志服务器,从而达到系统监控的目的呢? ...

January 15, 2019 · 1 min · jiezi

SpringBoot 日志框架

日志框架日志框架就是为了更好的记录日志使用的,记录日志是为了我们在工作中可以更好的查找相对应的问题,也算是以中留痕操作!以前我们刚开始学习的时候都是用System.out.println()去在控制台记录,怎么说呢?这种方式伴随着我们很长时间,之后我们就遇到了断点调试的方式,逐渐不在使用System.out.println()进行调试,但是你别忘记,那是一种记录不管是否有用,你都应该去记录!市面上的日志框架;JUL、JCL、Jboss-logging、logback、log4j、log4j2、slf4j….日志门面 (日志的抽象层)日志实现JCL(Jakarta Commons Logging) SLF4j(Simple Logging Facade for Java) jboss-loggingLog4j JUL(java.util.logging) Log4j2 LogbackSpringBoot:底层是Spring框架,Spring框架默认是用JCL;‘ <==SpringBoot选用 SLF4j和logback;==>1.SLF4j使用1.1 如何在系统中使用SLF4jSLF4J的官方网站手册以后开发的时候,日志记录方法的调用,不应该来直接调用日志的实现类,而是调用日志抽象层里面的方法;给系统里面导入slf4j的jar和 logback的实现jar使用方式如下:import org.slf4j.Logger;import org.slf4j.LoggerFactory;public class HelloWorld { public static void main(String[] args) { Logger logger = LoggerFactory.getLogger(HelloWorld.class); logger.info(“Hello World”); }}每一个日志的实现框架都有自己的配置文件。使用slf4j以后,配置文件还是做成日志实现框架自己本身的配置文件;1.2 历史遗留问题我们接触过的框架使用的日志框架都有所不同,因此,统一日志记录,即使是别的框架和我一起统一使用slf4j进行输出?1.3 slf4j统一“天下”将系统中其他日志框架先排除出去;用中间包来替换原有的日志框架;我们导入slf4j其他的实现;2. Spring Boot 日志配置SpringBoot使用它来做日志功能:<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId></dependency>SpringBoot底层也是使用slf4j+logback的方式进行日志记录SpringBoot也把其他的日志都替换成了slf4j中间替换包?@SuppressWarnings(“rawtypes”)public abstract class LogFactory { static String UNSUPPORTED_OPERATION_IN_JCL_OVER_SLF4J = “http://www.slf4j.org/codes.html#unsupported_operation_in_jcl_over_slf4j"; static LogFactory logFactory = new SLF4JLogFactory();如果我们要引入其他框架?一定要把这个框架的默认日志依赖移除掉示例:Spring框架用的是commons-logging;<dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <exclusions> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions></dependency>pringBoot能自动适配所有的日志,而且底层使用slf4j+logback的方式记录日志,引入其他框架的时候,只需要把这个框架依赖的日志框架排除掉即可;3. 日志的使用Spring Boot 默认给我们已经配置好了日志,测试如下://记录器Logger logger = LoggerFactory.getLogger(getClass());@Testpublic void contextLoads() { //System.out.println(); //日志的级别; //由低到高 trace<debug<info<warn<error //可以调整输出的日志级别;日志就只会在这个级别以以后的高级别生效 logger.trace(“这是trace日志…”); logger.debug(“这是debug日志…”); //SpringBoot默认给我们使用的是info级别的,没有指定级别的就用SpringBoot默认规定的级别;root级别 logger.info(“这是info日志…”); logger.warn(“这是warn日志…”); logger.error(“这是error日志…”);}3.1 日志格式说明 日志输出格式: %d表示日期时间, %thread表示线程名, %-5level:级别从左显示5个字符宽度 %logger{50} 表示logger名字最长50个字符,否则按照句点分割。 %msg:日志消息, %n是换行符 –> %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%nSpringBoot修改日志的默认配置logging.level.com.hanpang=trace# com.hanpang是说明的包名#logging.path=# 不指定路径在当前项目下生成springboot.log日志# 可以指定完整的路径;#logging.file=G:/springboot.log# 在当前磁盘的根路径下创建spring文件夹和里面的log文件夹;使用 spring.log 作为默认文件logging.path=/spring/log# 在控制台输出的日志的格式logging.pattern.console=%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n# 指定文件中日志输出的格式logging.pattern.file=%d{yyyy-MM-dd} === [%thread] === %-5level === %logger{50} ==== %msg%nlogging.filelogging.pathExampleDescription(none)(none) 只在控制台输出指定文件名(none)my.log输出日志到my.log文件(none)指定目录/var/log输出到指定目录的 spring.log 文件中3.2 指定配置给类路径下放上每个日志框架自己的配置文件即可,SpringBoot就不使用他默认配置的了Logging SystemCustomizationLogbacklogback-spring.xml, logback-spring.groovy, logback.xml or logback.groovyLog4j2log4j2-spring.xml or log4j2.xmlJDK (Java Util Logging)logging.propertieslogback.xml:直接就被日志框架识别了;一般情况不推荐(推荐)logback-spring.xml:日志框架就不直接加载日志的配置项,由SpringBoot解析日志配置,可以使用SpringBoot的高级Profile功能<springProfile name=“staging”> <!– configuration to be enabled when the “staging” profile is active –> 可以指定某段配置只在某个环境下生效</springProfile>示例代码<appender name=“stdout” class=“ch.qos.logback.core.ConsoleAppender”> <!– 日志输出格式: %d表示日期时间, %thread表示线程名, %-5level:级别从左显示5个字符宽度 %logger{50} 表示logger名字最长50个字符,否则按照句点分割。 %msg:日志消息, %n是换行符 –> <layout class=“ch.qos.logback.classic.PatternLayout”> <springProfile name=“dev”> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} —-> [%thread] —> %-5level %logger{50} - %msg%n</pattern> </springProfile> <springProfile name="!dev”> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ==== [%thread] ==== %-5level %logger{50} - %msg%n</pattern> </springProfile> </layout> </appender>如果使用logback.xml作为日志配置文件,还要使用profile功能,会有以下错误:no applicable action for [springProfile]4. 切换日志框架可以按照slf4j的日志适配图,进行相关的切换;slf4j+log4j的方式,pom.xml配置如下:<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <artifactId>logback-classic</artifactId> <groupId>ch.qos.logback</groupId> </exclusion> <exclusion> <artifactId>log4j-over-slf4j</artifactId> <groupId>org.slf4j</groupId> </exclusion> </exclusions></dependency><dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId></dependency>如果切换为log4j2,pom.xml配置如下:log4j2配置的参考文章log4j2的配置说明<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <artifactId>spring-boot-starter-logging</artifactId> <groupId>org.springframework.boot</groupId> </exclusion> </exclusions></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId></dependency>5.“坑”logging.path和logging.file不可以同时配置,同时配置也只有logging.file起效配置logging.path将会在指定文件夹下面生成spring.log文件,文件名字无法控制配置logging.file,如果只是文件名如:demo.log只会在项目的根目录下生成指定文件名的日志文件,,如果想控制日志路径,可以选择完整路径,如:E:\demo\demo.log接下来看看自定义配置文件,这个就要方便很多了,还是喜欢自定义配置文件的方式在src/main/resources下面新建文件logback.xml这个也是spring boot默认的配置文件名,如果需要自定义文件名,如:logback-test.xml需要在application.properties添加配置================但是,我们习惯使用logback-spring.xml==========================logging.config=classpath:logback-test.xmlspring boot默认载入的相关配置文件,详见jar包;spring-boot-1...RELEASE.jar下面org/springframework/boot/logging/logback/详细文件:base.xml //基础包,引用了下面所有的配置文件console-appender.xml //控制台输出配置defaults.xml //默认的日志文件配置file-appender.xml //文件输出配置附录logback-spring.xml 配置说明<?xml version=“1.0” encoding=“UTF-8”?><configuration> <!–定义日志文件的存储地址 勿在 LogBack的配置中使用相对路径 –> <property name=“LOG_HOME” value="/tmp/log" /> <!– 控制台输出 –> <appender name=“STDOUT” class=“ch.qos.logback.core.ConsoleAppender”> <encoder class=“ch.qos.logback.classic.encoder.PatternLayoutEncoder”> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{30} - %msg%n</pattern> </encoder> </appender> <!– 按照每天生成日志文件 –> <appender name=“FILE” class=“ch.qos.logback.core.rolling.RollingFileAppender”> <rollingPolicy class=“ch.qos.logback.core.rolling.TimeBasedRollingPolicy”> <FileNamePattern>${LOG_HOME}/logs/smsismp.log.%d{yyyy-MM-dd}.log</FileNamePattern> <!–日志文件保留天数 –> <MaxHistory>30</MaxHistory> </rollingPolicy> <encoder class=“ch.qos.logback.classic.encoder.PatternLayoutEncoder”> <!–格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 –> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{30} - %msg%n</pattern> </encoder> <!–日志文件最大的大小 –> <triggeringPolicy class=“ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy”> <MaxFileSize>10MB</MaxFileSize> </triggeringPolicy> </appender> <!– 日志输出级别 –> <root level=“INFO”> <appender-ref ref=“STDOUT” /> <appender-ref ref=“FILE” /> </root> <!– 定义各个包的详细路径,继承root宝的值 –> <logger name=“com.hry.spring.log” level=“INFO” /> <logger name=“com.hry.spring” level=“TRACE” /> <!– 此值由 application.properties的spring.profiles.active=dev指定–> <springProfile name=“dev”> <!–定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径 –> <property name=“LOG_HOME” value="/tmp/log" /> <logger name=“org.springboot.sample” level=“DEBUG” /> </springProfile> <springProfile name=“pro”> <!–定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径 –> <property name=“LOG_HOME” value="/home" /> <logger name=“org.springboot.sample2” level=“INFO” /> </springProfile> </configuration>部分说明appender name=“STDOUT”: 日志打印到控制台appender name=“FILE”: 日志按日打印到文件中,最多保留MaxHistory天,每个文件大水为MaxFileSizeencoder:定义输出格式<root level=“INFO”>: 定义根logger,通过appender-ref指定前方定义的appender<logger name=“com.hry.spring.log” level=“INFO” />:在继承root的logger上对com.hry.spring.log包日志作特殊处理<springProfile name=“dev”>: 定义profile的值,只有特定profile的情况下,此间定义的内容才启作用application.properties 启动dev配置信息 server.port=8080 spring.profiles.active=devspring.profiles.active指定本次启动的active的值是什么。本次是dev,则logback-spring.xml里<springProfile name=“dev”>的内容启作用import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplicationpublic class LogApplication { private static final Logger log = LoggerFactory.getLogger(LogApplication.class); public static void main(String[] args) { String str1 = “string1”; String str2 = “string2”; log.info(“Begin Start {}…{}”, str1, str2); SpringApplication.run(LogApplication.class, args); log.info(“Stop …”); }}logback-spring.xml 其他的写法<?xml version=“1.0” encoding=“UTF-8”?><configuration> <include resource=“org/springframework/boot/logging/logback/base.xml” /> <logger name=“org.springframework.web” level=“INFO”/> <logger name=“org.springboot.sample” level=“TRACE” /> <!– 测试环境+开发环境. 多个使用逗号隔开. –> <springProfile name=“test,dev”> <logger name=“org.springframework.web” level=“DEBUG”/> <logger name=“org.springboot.sample” level=“DEBUG” /> <logger name=“com.example” level=“DEBUG” /> </springProfile> <!– 生产环境. –> <springProfile name=“prod”> <logger name=“org.springframework.web” level=“ERROR”/> <logger name=“org.springboot.sample” level=“ERROR” /> <logger name=“com.example” level=“ERROR” /> </springProfile></configuration>这里说明一下:1) 引入的base.xml是Spring Boot的日志系统预先定义了一些系统变量的基础配置文件2) 在application.properties中设置环境为prod,则只会打印error级别日志3) 如果在application.properties中定义了相同的配置,则application.properties的日志优先级更高可以在内部进行引用<?xml version=“1.0” encoding=“utf-8”?><configuration scan=“true” scanPeriod=“10 seconds”> <!– 文件输出格式 –> <property name=“pattern” value="%d{yyyy-MM-dd HH:mm:ss.SSS} -%5p ${PID:-} [%15.15t] %-40.40logger{39} : %m%n" /> <property name=“charsetEncoding” value=“UTF-8” /> <!–<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>–> <!–控制台日志–> <appender name=“console” class=“ch.qos.logback.core.ConsoleAppender”> <encoder> <pattern>${pattern}</pattern> <charset>UTF-8</charset> </encoder> </appender> <appender name=“file” class=“ch.qos.logback.core.FileAppender”> <file>./logback/logfile.log</file> <append>true</append> <encoder> <pattern>${pattern}</pattern> <charset>${charsetEncoding}</charset> </encoder> </appender> <appender name=“dailyRollingFileAppender” class=“ch.qos.logback.core.rolling.RollingFileAppender”> <File>./logback/log.log</File> <rollingPolicy class=“ch.qos.logback.core.rolling.TimeBasedRollingPolicy”> <!– daily rollover –> <FileNamePattern>logback.%d{yyyy-MM-dd_HH}.log</FileNamePattern> <!– keep 30 days’ worth of history –> <maxHistory>7</maxHistory> </rollingPolicy> <encoder> <Pattern>${pattern}</Pattern> </encoder> </appender> <logger name=“org.springframework.web” level=“debug”/> <!– show parameters for hibernate sql 专为 Hibernate 定制 –> <logger name=“org.hibernate.type.descriptor.sql.BasicBinder” level=“TRACE” /> <logger name=“org.hibernate.type.descriptor.sql.BasicExtractor” level=“DEBUG” /> <logger name=“org.hibernate.SQL” level=“DEBUG” /> <logger name=“org.hibernate.engine.QueryParameters” level=“DEBUG” /> <logger name=“org.hibernate.engine.query.HQLQueryPlan” level=“DEBUG” /> <!–myibatis log configure–> <logger name=“com.apache.ibatis” level=“TRACE”/> <logger name=“java.sql.Connection” level=“DEBUG”/> <logger name=“java.sql.Statement” level=“DEBUG”/> <logger name=“java.sql.PreparedStatement” level=“DEBUG”/> <root level=“debug”> <appender-ref ref=“console”/> <appender-ref ref=“dailyRollingFileAppender”/> <appender-ref ref=“file”/> </root></configuration> ...

December 27, 2018 · 3 min · jiezi