Spring Boot Admin是一個開源社群項目,用于管理和監控SpringBoot應用程式, 線上檢視日志 修改日志級别等
目錄
-
- 使用場景
- 快速開始
-
- Spring Boot Admin 服務端
- Spring Boot Admin 用戶端
- Fastjson 相關問題
使用場景
當下網際網路技術發展很快,各類微服務程式的監控也很多,系統運作日志分布式管理的解決方案也很多 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: # 預警郵件通知 的 接受方,可以為多個
- [email protected]
- [email protected]
from: [email protected] # 同 mail.username
security:
user:
name: "admin" # 設定 spring boot admin 登入的使用者名
password: "middol123" # 設定 spring boot admin 登入的密碼
mail: # 設定 spring boot admin 預警郵件通知 的 發送方資訊,這裡以騰訊企業郵箱為例
host: smtp.exmail.qq.com
username: [email protected]
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 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 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:[email protected]" target="_blank" rel="external nofollow" >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;
}
}