监控服务常见功能使用

服务环境状态查看

监控面板展示微服务所在宿主机的磁盘、Java 运行环境、堆栈内存以及其他中间件的资源占用等信息。

服务监控面板

在线日志查看

服务部署在服务器上时,大部分公司都有对服务器的安全管理要求,开发人员通常无法登录服务器查看后台日志。这导致应用系统报错时,程序员无法快速定位错误。

通过 Spring Boot Admin(pigx-monitor)组件,可以在线查看后端服务日志,无需登录服务器即可进行问题排查。

配置 Logback 输出路径

Logback 日志路径配置

配置步骤:

  1. 修改目标微服务的 logback-spring.xml 文件
  2. 修改 log.path 为服务器实际的日志文件存储目录

配置 Monitor 日志加载

在监控服务中配置日志文件路径:

LOGGING_PATH: 对应如上图②处实际的日志文件存储目录
Monitor 日志路径配置
路径配置一致性

确保 Monitor 服务的 LOGGING_PATH 配置与微服务的 logback-spring.xml 中的 log.path 路径保持一致。

动态日志级别

应用默认的日志输出级别是 INFO,生产环境建议使用 ERROR 级别。

当遇到某些 Bug 时,动态调整日志输出级别非常有效。Spring Boot Admin 提供了在线调整日志级别的功能,无需重启服务即可调整日志输出详细程度。

动态日志级别调整
配置临时生效

目标服务重启后,动态调整的日志级别配置将失效,恢复为配置文件中的默认级别。

监控安全认证

默认情况下,Spring Boot Admin 不提供安全认证,任何用户都可以访问监控面板。为了保护监控信息,建议配置安全认证。

监控服务登录界面

添加安全依赖

在监控服务的 pom.xml 中添加 Spring Security 依赖:

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

创建 CustomCsrfFilter

创建自定义 CSRF 过滤器,处理跨站请求伪造防护:

package com.pig4cloud.pigx.monitor.config;

public class CustomCsrfFilter extends OncePerRequestFilter {

	public static final String CSRF_COOKIE_NAME = "XSRF-TOKEN";

	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

		CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName());

		if (csrf != null) {

			Cookie cookie = WebUtils.getCookie(request, CSRF_COOKIE_NAME);
			String token = csrf.getToken();

			if (cookie == null || token != null && !token.equals(cookie.getValue())) {
				cookie = new Cookie(CSRF_COOKIE_NAME, token);
				cookie.setPath("/");
				response.addCookie(cookie);
			}
		}

		filterChain.doFilter(request, response);
	}

}

创建 SecurityConfig

创建 Spring Security 配置类,定义安全策略:

package com.pig4cloud.pigx.monitor.config;


@Configuration(proxyBeanMethods = false)
public class SecuritySecureConfig {

    private final AdminServerProperties adminServer;

    private final SecurityProperties security;

    public SecuritySecureConfig(AdminServerProperties adminServer, SecurityProperties security) {
        this.adminServer = adminServer;
        this.security = security;
    }

    @Bean
    protected SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
        successHandler.setTargetUrlParameter("redirectTo");
        successHandler.setDefaultTargetUrl(this.adminServer.path("/"));

        http.authorizeHttpRequests((authorizeRequests) -> authorizeRequests //
                        .requestMatchers(new AntPathRequestMatcher(this.adminServer.path("/assets/**")))
                        .permitAll()
                        .requestMatchers(new AntPathRequestMatcher(this.adminServer.path("/actuator/info")))
                        .permitAll()
                        .requestMatchers(new AntPathRequestMatcher(adminServer.path("/actuator/health")))
                        .permitAll()
                        .requestMatchers(new AntPathRequestMatcher(this.adminServer.path("/login")))
                        .permitAll()
                        .dispatcherTypeMatchers(DispatcherType.ASYNC)
                        .permitAll() // https://github.com/spring-projects/spring-security/issues/11027
                        .anyRequest()
                        .authenticated())
                .formLogin(
                        (formLogin) -> formLogin.loginPage(this.adminServer.path("/login")).successHandler(successHandler))
                .logout((logout) -> logout.logoutUrl(this.adminServer.path("/logout")))
                .httpBasic(Customizer.withDefaults());

        http.addFilterAfter(new CustomCsrfFilter(), BasicAuthenticationFilter.class) // <5>
                .csrf((csrf) -> csrf.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
                        .csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler())
                        .ignoringRequestMatchers(
                                new AntPathRequestMatcher(this.adminServer.path("/instances"), POST.toString()), // <6>
                                new AntPathRequestMatcher(this.adminServer.path("/instances/*"), DELETE.toString()), // <6>
                                new AntPathRequestMatcher(this.adminServer.path("/actuator/**")) // <7>
                        ));

        http.rememberMe((rememberMe) -> rememberMe.key(UUID.randomUUID().toString()).tokenValiditySeconds(1209600));

        return http.build();

    }

    // Required to provide UserDetailsService for "remember functionality"
    @Bean
    public InMemoryUserDetailsManager userDetailsService(PasswordEncoder passwordEncoder) {
        UserDetails user = User.withUsername(security.getUser().getName())
                .password(passwordEncoder.encode(security.getUser().getPassword()))
                .roles("USER")
                .build();
        return new InMemoryUserDetailsManager(user);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}

配置登录用户

application.yml 中配置登录用户名和密码:

spring:
  security:
    user:
      name: pigx
      password: pigx
修改默认密码

生产环境请务必修改默认的用户名和密码,使用强密码策略以确保监控服务的安全性。