Spring Boot Admin是一个开源社区项目,用于管理和监控SpringBoot应用程序, 在线查看日志 修改日志级别等


目录



使用场景

当下互联网技术发展很快,各类微服务程序的监控也很多,系统运行日志分布式管理的解决方案也很多 ELK 、点评的 CAT等等。

但还有一些小型项目比如各内网小型系统,开发周期也很短的项目,为了这些项目搞个ELK 集群也是大费周章。

调研发现很早之前的Spring Boot Admin 这个开源项目不错,既能监控spring boot 程序也能在线查看日志,一举两得。

快速开始

分两部分服务端 + 客户端,客户端即为要被监控的Spring Boot 系统

Spring Boot Admin 服务端

当前 Spring Boot 2.3.8

当前 Spring Boot Admin 2.3.1

Spring Boot Admin 本身就是一个springboot 项目

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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- ============================== 引入统一版本控制父类xml ============================== -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>boot-admin</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>boot-admin</name>
<description>Demo project for Spring Boot</description>

<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>

<spring-boot-admin.version>2.3.1</spring-boot-admin.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-dependencies</artifactId>
<version>${spring-boot-admin.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>

</project>

Maven模块

说 明

spring-boot-admin-starter-server

spring-boot-admin 服务端需要引入的jar

spring-boot-starter-security

spring-boot-admin 需要登录才能查看日志监控信息,如果没有该模块则 任何人都能随意访问 admin

spring-boot-starter-mail

监控报警邮件通知

yaml 文件配置如下:

server:
port: 8888
spring:
application:
name: SpringBootAdmin
boot:
admin:
ui:
title: SpringBootAdmin-Server
notify:
mail:
to: # 预警邮件通知 的 接受方,可以为多个
- xxxxxxx@qq.com
- bbbbbb@qq.com
from: admin@qq.com # 同 mail.username
security:
user:
name: "admin" # 设置 spring boot admin 登录的用户名
password: "middol123" # 设置 spring boot admin 登录的密码
mail: # 设置 spring boot admin 预警邮件通知 的 发送方信息,这里以腾讯企业邮箱为例
host: smtp.exmail.qq.com
username: admin@qq.com
password: admin123456
port: 465
protocol: smtps

由于引入了security 模块,需要新建一个简单的配置类,来设置Spring boot admin的访问控制,放在可以被扫码到的包路径下,配置内容如下:

import de.codecentric.boot.admin.server.config.AdminServerProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;

@Configuration
public class SecuritySecureConfig extends WebSecurityConfigurerAdapter {

private final String adminContextPath;

public SecuritySecureConfig(AdminServerProperties adminServerProperties) {
this.adminContextPath = adminServerProperties.getContextPath();
}

@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
successHandler.setTargetUrlParameter( "redirectTo" );

http.authorizeRequests()
.antMatchers( adminContextPath + "/assets/**" ).permitAll()
.antMatchers( adminContextPath + "/login" ).permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage( adminContextPath + "/login" ).successHandler( successHandler ).and()
.logout().logoutUrl( adminContextPath + "/logout" ).and()
.httpBasic().and()
.csrf().disable();
// @formatter:on
}
}

启动类增加 @EnableAdminServer 注解

例如下面的示范:

import de.codecentric.boot.admin.server.config.EnableAdminServer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableAdminServer
public class BootAdminApplication {

public static void main(String[] args) {
SpringApplication.run(BootAdminApplication.class, args);
}

}


启动后, 访问 localhost:8888/ 得到如下界面,输入用户名密码登入即可:

spring boot admin 监控实践_监控

现在还没有客户端注册上来,我们接下来创建需要监控的客户端

spring boot admin 监控实践_admin_02

Spring Boot Admin 客户端

当前 Spring Boot 2.3.8

当前 Spring Boot Admin 2.3.1

客户端(或你本身已经有待监控的SpringBoot项目)引入如下maven依赖即可:

   <!--  actuator 需要暴露出的系统相关性能监控信息给 Spring boot admin -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
</dependency>

这里的测试客户端完整 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 https://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.3.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>boot-admin-client</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>boot-admin-client</name>
<description>Demo project for Spring Boot</description>

<properties>
<java.version>1.8</java.version>
<spring-boot-admin.version>2.3.1</spring-boot-admin.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-dependencies</artifactId>
<version>${spring-boot-admin.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>

</project>

下一步主要设置 application.yml 文件, 这里测试客户端的完整 配置如下:


server:
port: 8083
tomcat:
uri-encoding: UTF-8 # tomcat的URI编码
threads:
max: 1000 # tomcat最大线程数,默认为200
min-spare: 30 # Tomcat启动初始化的线程数,默认值25
servlet:
context-path: /${spring.application.name}
encoding:
charset: UTF-8
enabled: true
force: true
shutdown: graceful # 开启优雅停机模式

spring:
application:
name: boot-admin-client2
lifecycle:
timeout-per-shutdown-phase: 30s # 优雅停机模式,设置缓冲时间,最大关机等待时间
servlet:
multipart:
enabled: true
max-file-size: 20MB
max-request-size: 200MB
jackson:
time-zone: GMT+8
boot:
admin:
client:
url: http://localhost:8888 # spring boot admin 地址
instance:
service-base-url: http://localhost:${server.port} # 在 spring boot admin 控制面板中展示的client地址
username: admin # spring boot admin 登录认证的用户名密码
password: middol123 # spring boot admin 登录认证的用户名密码

management:
endpoint:
logfile: # 需要设置该值为 true 才能在 spring boot admin 中查看日志
enabled: true
shutdown:
enabled: false
health:
show-details: always
endpoints: # 需要设置 该值,spring boot admin 才能监控检查本系统
web:
exposure:
include: "*"

# 设置 logging 信息才能 让 spring boot admin 读取到 log 日志文件内容
logging:
pattern: # 设置彩色日志信息
file: "%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%5p) %clr(${PID}){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n%wEx"
file:
name: logs/${spring.application.name}.log # 这里参考 官方文档 ,设置日志的路径和名称
max-history: 7
max-size: 10MB

启动应用,查看Spring boot admin,当启动成功可以看到控制台有一句日志:

2021-01-17 20:39:54.725  INFO 24456 --- [gistrationTask1] d.c.b.a.c.r.ApplicationRegistrator       : Application registered itself as d30fe4656560

这样表示 注册到 Spring boot admin 成功

查看spring boot admin 系统界面如下:

spring boot admin 监控实践_admin_03

点击 这个绿色的六边形图案,进入监控界面

spring boot admin 监控实践_spring boot_04

各类运行系统参数都有,点击日志查看在线日志:

spring boot admin 监控实践_admin_05

到此, 快速实践完毕,关于其他更多内容(例如集成 Spring cloud)请查看官方文档:

​https://codecentric.github.io/spring-boot-admin/2.3.1/​

Fastjson 相关问题

如果你的 Spring boot 采用 fastjson 作为首选 HttpMessageConverter 的话,需要注意一下 有个 MediaType 需要忽略掉。

  /**
* Public constant media type for {@code text/plain}.
*/
public static final MediaType TEXT_PLAIN;

该 MediaType (text/plain) 如果也被Fastjson 支持解析成JSON的话,Spring boot admin 取 log日志文件内容的时候希望是文本信息,不是JSON格式的信息,因此 会报 HTTP 416 错误

因此需要将 TEXT_PLAIN 这种 MediaType 忽略掉。


import cn.hutool.core.date.DatePattern;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.serializer.ToStringSerializer;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

/**
* 一般的 FastJsonHttpMessageConverter 配置
* @author <a href="mailto:admin@2235m.com">guzhongtao</a>
*/
@Configuration
public class MyFastJsonConfig {

@Bean
FastJsonHttpMessageConverter fastJsonHttpMessageConverter() {
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
List<MediaType> supportedMediaTypes = new ArrayList<>();
supportedMediaTypes.add(MediaType.APPLICATION_JSON);
supportedMediaTypes.add(MediaType.APPLICATION_ATOM_XML);
supportedMediaTypes.add(MediaType.APPLICATION_FORM_URLENCODED);
supportedMediaTypes.add(MediaType.APPLICATION_OCTET_STREAM);
supportedMediaTypes.add(MediaType.APPLICATION_PDF);
supportedMediaTypes.add(MediaType.APPLICATION_RSS_XML);
supportedMediaTypes.add(MediaType.APPLICATION_XHTML_XML);
supportedMediaTypes.add(MediaType.APPLICATION_XML);
supportedMediaTypes.add(MediaType.IMAGE_GIF);
supportedMediaTypes.add(MediaType.IMAGE_JPEG);
supportedMediaTypes.add(MediaType.IMAGE_PNG);
supportedMediaTypes.add(MediaType.TEXT_EVENT_STREAM);
supportedMediaTypes.add(MediaType.TEXT_HTML);
supportedMediaTypes.add(MediaType.TEXT_MARKDOWN);

// --------------------------------------------------------------------
// 需要将 TEXT_PLAIN 忽略掉 ,注释掉
// supportedMediaTypes.add(MediaType.TEXT_PLAIN);
// --------------------------------------------------------------------

supportedMediaTypes.add(MediaType.TEXT_XML);
converter.setSupportedMediaTypes(supportedMediaTypes);

FastJsonConfig config = new FastJsonConfig();
config.setDateFormat(DatePattern.NORM_DATETIME_PATTERN);
config.setCharset(StandardCharsets.UTF_8);

config.setSerializerFeatures(
SerializerFeature.WriteMapNullValue,
SerializerFeature.WriteNullListAsEmpty,
SerializerFeature.PrettyFormat,
SerializerFeature.WriteNullStringAsEmpty,
SerializerFeature.DisableCircularReferenceDetect
);

//解决Long转json精度丢失的问题
SerializeConfig serializeConfig = SerializeConfig.globalInstance;
serializeConfig.put(BigInteger.class, ToStringSerializer.instance);
serializeConfig.put(Long.class, ToStringSerializer.instance);
serializeConfig.put(Long.TYPE, ToStringSerializer.instance);
config.setSerializeConfig(serializeConfig);

converter.setFastJsonConfig(config);
return converter;
}
}