spring boot admin 监控实践

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-serverspring-boot-admin 服务端需要引入的jar
spring-boot-starter-securityspring-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 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: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;
    }
}


版权声明:本文为gzt19881123原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。