此文档是有关于springboot配置文件,监控,日志部分基础知识
配置文件
上文我们说到,src/main/resources目录是springboot的配置目录,所以当要为应用创建个性化配置时,应在该目录下进行。
Springboot的默认配置文件位置在src/main/resources/application.properties。关于springboot的应用配置内容都可以集中在该文件中,根据我们引入的不同starter模块,可以在这里定义容器端口号,数据库连接信息,日志级别等各种配置信息。比如,我们需要自定义web模块的服务端口号,可以在application.properties中添加server.port=8888来指定服务端口为8888,也可以通过spring.application.name = hello来指定应用名(该名字在后续springcloud中被注册为服务名)。
YAML文件
1.简单介绍:
Springboot的配置文件除了可以使用传统的properties文件外,还支持现在被广泛推荐使用的YAML文件。YAML采用的配置格式不是键值对的形式,而是以类似大纲的缩进形式来表示,例如:
environments:
dev:
url:http://dev.bar.com
name:Developer Setup
prod:
url:http://foo.bar.com
name:My Cool App
//与其等价的properties配置如下所示:
environments.dev.url=http://dev.bar.com
environments.dev.Name=Developer Setup
environments.Prod.Url=http://foo.bar.com
environments.Prod.Name=My Cool App
1.配置信息利用阶梯化缩进方式,结构更为清晰易读
2.配置内容字符量显著减少
3.YAML还可以在一个单个文件中通过使用spring.properties属性来定义多个不同的环境配置。
例如:test环境时,server使用8882端口;prod环境时,使用8883端口,默认使用8881端口:
Server:
Port:8881
_ _ _
Spring:
Profiles:test
Server:
Prot:8883
_ _ _
Spring:
Profiles:prod
Server:
Port:8883
目前,YAML无法通过@PropertySource注解来加载配置。但是YAML将属性加载到内存中保存的时候是有序的,所以当配置文件中的信息需要具备顺序含义时,YAML配置方式比PROPERTIES配置文件更为有优势
2.使用
Spring框架提供了两个方便的类,可用于负载YAML文件。YamlPropertiesFactoryBean 将负载YAML 属性 和 YamlMapFactoryBean 将负载YAML成一个MAP.
例如,下面的YAML文件:
environments:
dev:
url: http://dev.bar.com
name: Developer Setup
prod:
url: http://foo.bar.com
name: My Cool App
将转换为这些属性:
environments.dev.url=http://dev.bar.com
environments.dev.name=Developer Setup
environments.prod.url=http://foo.bar.com
environments.prod.name=My Cool App
YAML列表表示为属性键 (指数) dereferencers, 例如这个YAML:
my:
servers:
- dev.bar.com
- foo.bar.com
将转换为这些属性:
my.servers[0]=dev.bar.com
my.servers[1]=foo.bar.com
To bind to properties like that using the Spring DataBinder utilities (which is what @ConfigurationProperties does) you need to have a property in the target bean of type java.util.List (or Set) and you either need to provide a setter, or initialize it with a mutable value, e.g. this will bind to the properties above
@ConfigurationProperties(prefix="my")
public class Config {
private List<String> servers = new ArrayList<String>();
public List<String> getServers() {
return this.servers;
}
}
需要格外小心当配置列表方式覆盖不会为你工作 所期望的。 在上面的示例中,当 my.servers 重新定义了在几个地方, 单个元素是针对覆盖,而不是列表中。 确保 PropertySource 优先级高的可以覆盖列表,您需要定义它 一个属性:
my:
servers: dev.bar.com,foo.bar.com
3.在spring的环境中作为一个属性暴露
YamlPropertySourceLoader 类可以使用YAML公开为一个 PropertySource 在spring 环境 。 这允许您使用熟悉的 @ value 注释与 占位符语法访问YAML属性。
YAML是JSON的一个超集, 可以非常方便的将外部配置以层次结构形式存储起来。 比如:
spring:
application:
name: cruncher
datasource:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost/test
server:
port: 9000
创建一个application.yml文件, 将它放到classpath的根目录下, 并添加snakeyaml依赖(Maven坐标为 org.yaml:snakeyaml ,
如果你使用 spring-boot-starter 那就已经被包含了) 。 一个YAML文件会被解析为一个Java Map
spring.application.name=cruncher
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost/test
server.port=9000
自定义参数
【Ps:
SpringApplication将从application.properties以下位置的文件加载属性,并将它们添加到Spring中Environment:
一个/config当前目录下的子目录。
当前目录
一个classpath /config包
类路径根
该列表按优先级排序(在列表中定义的属性中定义的属性将覆盖在较低位置中定义的位置)。
您也可以使用YAML(’.yml’)文件替代“.properties”。】
除了可以在springboot的配置文件中设置各个starter模块中预定义的配置属性,也可以在配置文件中定义一些我们需要的自定义属性,比如在application.properties中添加:
project.name=SpringBootDemo
project.author=cc
1.在应用中可以通过@Value注解来加载这些自定义的参数
package com.cc.springbootDemo.web;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
/*
* @Value注解加载属性值的时候可以支持两种表达式来进行配置
* 1.PlaceHolder方式,格式为${...},大括号内为PlaceHolder
* 2.SpEl表达式,格式为#{...},大括号内为SpEL表达式
*/
@Value("${project.name}")
private String name;
@RequestMapping("/handleProperties")
public String handleProperties() {
return this.name;
}
}
2.还可以通过environment.getProperty(key)的方式获取value
@Autowired
private Environment environment;
environment.getProperty("project.author")
访问http://localhost:8080/handleProperties:
springbootDemo
3.也可以通过spring的方式@Configuration标明这是一个配置类,用@Value引入这些参数
@Configuration
public class Project {
/*
* @Value注解加载属性值的时候可以支持两种表达式来进行配置
* 1.PlaceHolder方式,格式为${...},大括号内为PlaceHolder
* 2.SpEl表达式,格式为#{...},大括号内为SpEL表达式
*/
@Value("${project.name}")
private String name;
@Value("${project.author}")
private String author;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
}
@RestController
public class HelloController {
@Autowired
private Project project;
public Project getProject() {
return project;
}
public void setProject(Project project) {
this.project = project;
}
@RequestMapping("/handleProperties")
public String handleProperties() {
return "@Autowired-project="+project.getAuthor();
}
}
访问http://localhost:8080/handleProperties:
@Autowired-project=cc
参数引用
在application.properties中各个参数可以直接通过使用PlaceHolder的方式进行引用,例如:
project.desc=${project.author} is writing ${project.name}
把刚才的@Value注解改为
@Value("${project.desc}")
再次访问http://localhost:8080/handleProperties:
cc is writing springbootDemo
使用随机数
我们希望有些参数每次被加载的时候不是一个固定的值,比如秘钥,服务端口等。可以使用${random}来产生随机的int,long或者String字符串。这样可以避免硬编码方式,而是配置的方式产生随机属性:
随机字符串:random.value随机int: {random.int}
随机long:random.long10以内随机int: {random.int(10)}
10~20的随机数int:${random.int[10,20]}
该方式可以设置应用端口等场景,避免在本地调试时出现端口冲突的麻烦
命令行参数
Java -jar xxx.jar --server.port=8888
可以直接以命令行的方式来设置server.port的属性,并将应用启动端口设为8888
此时连续两个减号–就是对application.properties中的属性值进行赋值的标识
多环境配置
对于多环境的配置,各项目构建工具或是框架的基本思路是一致的,通过配置多份不同环境的配置文件,再通过打包命令指定需要打包的内容之后进行区分打包。
Springboot中的配置:
多环境的文件名需要满足application-{profile}.properties的格式,其中{profile}对应你的环境标识。
Application-dev.properties:开发环境
Application-test.properties:测试环境
Application-prod.properties:生产环境
至于哪个配置文件会被加载,需要在application.properties文件中通过spring.profiles.active属性来设置,值对应{profile}的值,如果spring.profiles.active=test,就会去加载Application-test.properties中的属性
示例:
Application-dev.properties:
project.name=springbootDemo-dev
project.author=cc
project.desc=${project.author} is writing ${project.name}
Application-test.properties:
project.name=springbootDemo-test
project.author=cc
project.desc=${project.author} is writing ${project.name}
application.properties:
设置启动test配置
spring.profiles.active=test
再次访问http://localhost:8080/handleProperties:
cc is writing springbootDemo-test
可以用来给测试环境和开发环境设置不同的端口号等。
加载顺序
多环境配置中发现,配置信息暴露给了开发人员,显然不是很好,而且每次要去改程序才可以,也非常不方便。因此,出现了许多配置内容外部化的框架和工具,比如spring cloud config,
首先需要了解springboot对数据文件的加载机制。
加载顺序:
1.命令行中传入的参数
2.SPRING_APPLICATION_JSON中的属性。SPRING_APPLICATION_JSON是以JSON格式配置在系统环境变量中的内容
3.Java:comp/env中的JNDI属性
4.Java的系统属性,可以通过System.getProperties()获得的内容
5.操作系统的环境变量
6.通过ramdom.*配置的随机属性
7.位于当前应用jar包之外,针对不同{profile}环境配置文件内容,例如application-{profile}.properties或是YAML定义的配置文件
8.位于当前应用jar包之内,针对不同{profile}环境配置文件内容,例如application-{profile}.properties或是YAML定义的配置文件
9.位于当前应用jar包之外的application.properties和YAML配置的内容
10.位于当前应用jar包之内的application.properties和YAML配置的内容
11.在@Configuration注解修改的类中,通过@PropertiesSource注解定义的属性
12.应用默认属性,使用springApplication.setDefaultProperties定义的内容
以上优先级由高到低,数字越小优先级越高。
由此发现,外部配置文件的加载顺序优先级高于内部,所以可以指定外部配置文件的加载位置来取代内部的内容。通过这样的实现,我们开发就会变得非常干净,只需要在本地配置开发需要的配置即可,不需要关系其他环境的配置,由其对应环境的负责人去维护即可。
2.监控与管理
我们需要一套自动化的监控运维机制,运行基础就是不断地手机各个微服务应用的各项指标情况,并根据这些基础指标信息来指定监控和预警规则,更进一步甚至做到一些自动化的运维操作等。
我们需要开发一套专门用于植入各个微服务应用的接口供监控系统采集信息。这些接口往往很大一部分指标都是类似的,比如环境变量,垃圾收集信息,内存信息,线程池信息等。这些信息如此通用,所以会有一些标准化的实现架构。
我们决定使用springboot来作为微服务框架时,出了他强大的快速开发功能之外,还因为它在Starter POMS中提供了一个特殊依赖模块spring-boot-starter-actuator。引入该模块能够自动为springboot构建的应用提供一系列用于监控的端点。同时springcloud在实现各个微服务组件的时候,进一步为该模块做了不少的扩展。
对于中小团队来说,可以省下不少开发量。当然,有的时候也不是万能的,需要我们自己扩展一些个性化需求。
初始actuator
示例:
1.现有的web项目中引入该模块非常简单,只需要在pom.xml的依赖节点中,新增spring-boot-sarter-actuator的依赖即可。
<!-- 监控 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
启动项目显示如下:
这些不是我们程序中自己编码出来的,而是spring-boot-starter-actuator模块根据应用依赖和配置自动创建出来的监控和管理端点,我们可以实时获取应用的监控各项指标,例如访问/health(http://localhost:8080/health)端点,返回如下信息:
{“status”:”UP”}
当然IE浏览器默认它是一个文件返回,提示你下载保存,我用的是搜狗浏览器
在没有引入其他依赖前,它非常简单,后续在使用spring cloud各个组件后会丰富起来,这些内容将帮助我们指定更为个性化的监控策略。
原生端点:
分为三类:
应用配置类:获取应用程序中加载的应用配置,环境变量,自动化配置报告等与springboot应用密切相关的配置类信息
度量指标类:获取应用程序运行过程中用于监控的度量指标。比如内存信息,线程池信息,HTTP请求统计等。
操作控制类:提供了对应用的关闭等操作类功能
应用配置类:
帮助我们轻松获取一系列关于spring应用配置内容的详细报告,比如自动化配置的报告,bean创建的报告,环境属性的报告等。
1./autoconfig:帮助我们找到一些自动化配置为什么没有生效的具体原因。posotiveMathces返回条件匹配成功的自动化配置,negativeMatches返回条件不成功的自动化配置。
当我们期望的配置没有生效时,可以来这个端点看看为什么没有生效。
2./beans:上下文中创建的所有Bean.
Bean:名称
Scope:bean的作用域
Type:Bean的java类型
Resource:class文件的具体路径
Dependencies:依赖的Bean名称
3./configprops:获取应用中配置的属性信息报告
4./env:获取应用中所有可用的环境属性报告:JVM属性,命令行参数,环境变量等
5./mappings:所有springMVC的控制器映射关系报告。映射关系的请求处理器以及处理类和处理函数。
6./info:应用自定义信息。默认返回一个空的JSON内容。我们可以在application.properties配置文件中通过info前缀来设置一些属性。
例:
#info actualtor
info.app.name = spring-boot-hello
info.app.version = v1.0.0
访问:http://localhost:8080/info
{“app”:{“name”:”spring-boot-hello”,”version”:”v1.0.0”}}
度量指标类:
上面的应用配置类所提供的信息报告在应用启动的时候就已经基本确定了其返回内容,可以说是一个静态报告。而度量指标类端点提供的报告内容则是动态变化的。这些端点提供了应用程序在运行过程中的一些快照信息,比如内存使用情况,HTTP请求统计,外部资源指标等。这些端点对于我们构建微服务框架中的监控系统非常有帮助,由于springboot应用自身实现了这些端点,所以我们可以很方便地利用他们来收集我们想要的信息,制定出各种自动化策略。
1./metrics:返回当前应用各类重要度量指标,比如内存信息,线程信息,垃圾回收信息等。
2./dump:暴露程序运行中的线程信息
3./trace:返回基本的HTTP跟踪信息
操作控制类:
原生端点中只提供了一个/shutdown端点,访问即可实现关闭该应用的远程操作,所以实际应用的时候就要进行一系列安全措施。
3.@SpringBootApplication注释
开发者总是有其主类注解为@Configuration, @EnableAutoConfiguration和@ComponentScan。由于这些注释是如此频繁地使用(特别是如果您遵循上述最佳实践 ),Spring Boot提供了一个方便的@SpringBootApplication替代方法。
该@SpringBootApplication注解相当于使用@Configuration, @EnableAutoConfiguration并@ComponentScan与他们的默认属性
日志
默认情况下, 如果你使用’Starter POMs’, 那么就会使用Logback记录日志。 为了确保那些使用Java Util Logging, Commons,Logging, Log4J或SLF4J的依赖库能够正常工作, 正确的Logback路由也被包含进来。
注:如果上面的列表看起来令人困惑, 不要担心, Java有很多可用的日志框架。 通常, 你不需要改变日志依赖, Spring Boot默认的就能很好的工作。
日志输出:
2017-08-25 14:56:25.163 INFO 10352 --- [ main] o.s.c.support.DefaultLifecycleProcessor : Starting beans in phase 0
1.日期和时间
2.日志级别
3.Process ID
4.—分隔符
5.线程名-方括号中
6.日志名,通常是class的类名
7.日志信息
控制台输出:
默认的日志配置会在写日志消息时将它们回显到控制台。 默认, ERROR, WARN和INFO级别的消息会被记录。 可以在启动应用时, 通过 –debug 标识开启控制台的DEBUG级别日志记录。
$ java -jar myapp.jar --debug
如果你的终端支持ANSI, 为了增加可读性将会使用彩色的日志输出。 你可以设置 spring.output.ansi.enabled 为一个支持的值来覆盖自动检测。
文件输出:
默认情况下, Spring Boot只会将日志记录到控制台而不会写进日志文件。 如果除了输出到控制台你还想写入到日志文件, 那你需要设置 logging.file 或 logging.path 属性( 例如在你的application.properties中) 。
表显示如何组合使用 logging.* :
logging.file logging.path 示例 描述
(none) (none) 只记录到控制台
Specific file (none) my.log 写到特定的日志文件里, 名称可以是一个精确的位置或相对于当前目
录
(none) Specific
folder /var/log 写 于到 当特 前定 目文 录件夹下的spring.log里, 名称可以是一个精确的位置或相对
日志文件每达到10M就会被轮换( 分割) , 和控制台一样, 默认记录ERROR, WARN和INFO级别的信息。
日志级别:
所有支持的日志系统在Spring的Environment( 例如在application.properties里) 都有通
过’logging.level.*=LEVEL’( ‘LEVEL’是TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF中的一个) 设置的日志级别。
示例:application.properties
logging.level.org.springframework.web: DEBUG
logging.level.org.hibernate: ERROR
示例:
#log on a file not console
logging.file=my.log
logging.level.org.springframework.web: DEBUG
http://localhost:8080/hello访问后:
控制台会有输出,同时当前的项目下出现了:
打开也会有记录。
自定义日志配置:
通过将适当的库添加到classpath, 可以激活各种日志系统。 然后在classpath的根目录(root)或通过Spring Environment的 logging.config 属性指定的位置提供一个合适的配置文件来达到进一步的定制( 注意由于日志是在ApplicationContext被创建之前初始化的, 所以不可能在Spring的@Configuration文件中, 通过@PropertySources控制日志。 系统属性和平常的Spring Boot外部配置文件能正常工作) 。
根据你的日志系统, 下面的文件会被加载:
日志系统 定制
Logback logback.xml
Log4j log4j.properties或log4j.xml
Log4j2 log4j2.xml
JDK (Java Util Logging) logging.properties
为了帮助定制一些其他的属性, 从Spring的Envrionment转换到系统属性:
Spring
Environment
System
Property 评价
logging.file LOG_FILE 如果定义, 在默认的日志配置中使用
logging.path LOG_PATH 如果定义, 在默认的日志配置中使用
PID PID 当前的处理进程(process)ID( 如果能够被发现且还没有作为操作系统环境
变量被定义)
所有支持的日志系统在解析它们的配置文件时都能查询系统属性。 具体可以参考spring-boot.jar中的默认配置。
注:在运行可执行的jar时, Java Util Logging有类加载问题, 我们建议你尽可能避免使用它。
示例:
上面说的是默认这些框架的名称是这样的话,springboot就能够自动加载入环境,默认的父依赖中的日志是logback,下面我们自定义实现一个日志。
1.Src/main/resource下自定义一个配置文件:
logging-config.properties
2.application.properties中自定义我们的框架配置文件为logging-config.properties:
#self log pattern not default (it is logback here)
logging.config=classpath:logging-config.properties
3.logging-config.properties:(自定义任意一个日志配置)
log4j.rootLogger=DEBUG, Console
#LoginFilter
log4j.category.com.yan.access.filter.LoginFilter=DEBUG, mainLog
log4j.additivity.com.yan.access.filter.LoginFilter=false
log4j.appender.mainLog=org.apache.log4j.DailyRollingFileAppender
log4j.appender.mainLog.MaxFileSize=10MB
log4j.appender.mainLog.MaxBackupIndex=5
log4j.appender.mainLog.File=/mainLog.log
log4j.appender.mainLog.DatePattern='.'yyyyMMdd'.log'
log4j.appender.mainLog.layout=org.apache.log4j.PatternLayout
log4j.appender.mainLog.layout.ConversionPattern=%m%n
4.添加log4j的依赖:
<!-- 1.去除默认依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 2.添加 log4j 依赖 -->
<!-- 添加 log4j 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j</artifactId>
<version>1.3.8.RELEASE</version>
</dependency>
运行后:
log4j:WARN No appenders could be found for logger (org.springframework.web.context.support.StandardServletEnvironment).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
发现已经变为log4j的日志配置