SpringBoot Monitoring Artifact——Actuator Nanny-Level Tutorial

SpringBoot has its own monitoring function Actuator, which can help monitor the internal operation of the program, such as monitoring status, Bean loading status, environment variables, log information, thread information, etc.

Configure Actuators

pom.xml

<!-- web start-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- web end-->
<!-- actuator start-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- actuator end-->

browser opens the linkhttp://localhost:8080/actuator/

You can see all supported connections, the default is only

/actuator
/actuator/health
/actuator/health/{component}
/actuator/health/{component}/{instance}
/actuator/info

bean loadinghttp://localhost:8080/actuator/beans

Specific usage method:

  • Introduce the above dependent jar

Enable all monitoring endpoints with the following configuration, which are disabled by default;

management:
  endpoints:
    web:
      exposure:
        include: "*"

"*" stands for enabling all monitoring endpoints, which can be enabled individually, for example, health, info, metricsetc.

Actuator's REST interface

Actuator monitoring is divided into two categories: native endpoints and user-defined endpoints; custom endpoints mainly refer to scalability. Users can define some indicators of concern according to their actual applications and monitor them during runtime.

The native endpoint is to provide many web interfaces in the application, and through them to understand the internal conditions of the application when it is running. Native endpoints can be further divided into three categories:

  • Application configuration class : You can view the static information of the application during runtime: such as automatic configuration information, loaded springbean information, yml file configuration information, environment information, and request mapping information;

  • Metric category : mainly dynamic information during runtime, such as stack, request connection, some health indicators, metrics information, etc.;

  • Operation control class : mainly refers to shutdown, the user can send a request to close the monitoring function of the application.

Actuator provides 13 interfaces, as shown in the following table.

Detailed command

For security reasons in Spring Boot 2.x, Actuator only opened two endpoints  /actuator/health and  /actuator/info. Can be set to open in the configuration file.

All monitoring points can be opened

management:
  endpoints:
    web:
      exposure:
        include: "*"

You can also choose to open the section

management:
  endpoints:
    web:
      exposure:
        exclude: beans,trace

Actuator has all monitoring point paths by default /actuator/*, and of course, this path also supports customization if necessary.

management:
  endpoints:
    web:
      base-path: /manage #记得要加上/

After setting and restarting, the access address will become/manage/*

Actuator monitors almost all aspects of the application, and we focus on some commands that are often used in projects.

  • health

health is mainly used to check the running status of the application, which is the most frequently used monitoring point. This interface is usually used to remind us of the running status of the application instance and the reason why the application is not "healthy", such as database connection, insufficient disk space, etc.

By default, the state of health is open. After adding dependencies, start the project and visit: http://localhost:8080/actuator/healthto see the state of the application.

{
    "status" : "UP"
}

If you want to view detailed application health information, you need to configure management.endpoint.health.show-details the value of always. After the configuration, we visit again http://localhost:8080/actuator/health, and the obtained information is as follows:

{
    "status": "UP",
    "components": {
        "diskSpace": {
            "status": "UP",
            "details": {
                "total": 236510507008,
                "free": 223361744896,
                "threshold": 10485760
            }
        },
        "ping": {
            "status": "UP"
        }
    }
}

The health information of Spring boot ApplicationContextis HealthIndicator Beanscollected from various sources. The Spring boot framework contains a large number of HealthIndicatorsimplementation classes. Of course, you can also achieve the health status you think.

By default, the state of the final Spring Boot application is  HealthAggregator summarized, and the summary algorithm is:

  1. Set status code sequence: setStatusOrder(Status.DOWN, Status.OUT_OF_SERVICE, Status.UP, Status.UNKNOWN);.

  2. Filter out unrecognized status codes.

  3. If there is no status code, the status of the entire Spring Boot application is  UNKNOWN.

  4. Sort all collected status codes in the order of 1.

  5. Returns the first status code in an ordered sequence of status codes as the status of the entire Spring Boot application.

health Checks the health of the application by combining several health indices. The ones that come with the Spring boot framework  HealthIndicators currently include:

For example, if your application uses Redis, RedisHealthindicator it will be considered as part of the check; if it uses MongoDB, MongoHealthIndicator it will be considered as part of the check.

You can turn off specific health check indicators in the configuration file, such as turning off redis health check:

management:
  health:
    redise:
      enabled: false

By default, all of these health indicators are considered part of the health check.

  • Custom HealthIndicator health check

Sometimes you need to provide custom health status check information, you can HealthIndicatorimplement it through the implemented interface, and register the implementation class as a spring bean. You need to implement one of the health()methods and return a custom health status response message, which should include a status code and detailed information to display. For example, the following is an HealthIndicatorimplementation class of an interface:

@Component
public class MyHealthIndicator implements HealthIndicator {

    @Override
    public Health health() {
        int errorCode = check(); // perform some specific health check
        if (errorCode != 0) {
            return Health.down().withDetail("Error Code", errorCode).build();
        }
        return Health.up().build();
    }

}

In addition, in addition to several state types defined by Spring boot, we can also customize state types to represent a new system state. In this case, you also need to implement the interface  HealthAggregator , or configure the default implementation  management.health.status.order to continue using .HealthAggregator

For example, in your custom health check HealthIndicatorimplementation class, a custom status type is used FATAL. In order to configure the severity of the status type, you need to add the following configuration to the application configuration file:

management:
  health:
    status:
      order: FATAL, DOWN, OUT_OF_SERVICE, UNKNOWN, UP

When doing a health check, the HTTP status code in the response reflects the overall health status, (for example, UP corresponds to 200, and UP  OUT_OF_SERVICE corresponds  DOWN to 503). Similarly, you also need to set the corresponding HTTP status code for the custom status type. For example, the following configuration can be  FATAL mapped to 503 (service unavailable):

management:
  health:
    status:
      http-mapping:
        FATAL: 503

The following is a list of HTTP status codes corresponding to the built-in health status types:

info

info is the configuration information that we configure ourselves starting with info in the configuration file. For example, our configuration in the sample project is:

info:
  app:
    name:
      spring-boot-actuator
    version: 1.0.0
    test: test

Start the sample project, visit: http://localhost:8080/actuator/infoReturn part of the information is as follows:

{
  "app": {
    "name": "spring-boot-actuator",
    "version": "1.0.0",
    "test":"test"
  }
}

beans

According to the example, it can be seen that the bean's alias, type, whether it is a singleton, class address, dependency and other information are displayed.

Start the sample project, visit: http://localhost:8080/actuator/beansReturn part of the information is as follows:

[
  {
    "context": "application:8080:management",
    "parent": "application:8080",
    "beans": [
      {
        "bean": "embeddedServletContainerFactory",
        "aliases": [
          
        ],
        "scope": "singleton",
        "type": "org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory",
        "resource": "null",
        "dependencies": [
          
        ]
      },
      {
        "bean": "endpointWebMvcChildContextConfiguration",
        "aliases": [
          
        ],
        "scope": "singleton",
        "type": "org.springframework.boot.actuate.autoconfigure.EndpointWebMvcChildContextConfiguration$$EnhancerBySpringCGLIB$$a4a10f9d",
        "resource": "null",
        "dependencies": [
          
        ]
      }
  }
]

conditions

The automatic configuration function of Spring Boot is very convenient, but sometimes it means that it is difficult to find out the specific cause of the problem. Using conditions, you can view the code under what conditions a certain configuration takes effect when the application is running, or why a certain automatic configuration does not take effect.

Start the sample project, visit: http://localhost:8080/actuator/conditionsReturn part of the information is as follows:

{
    "positiveMatches": {
     "DevToolsDataSourceAutoConfiguration": {
            "notMatched": [
                {
                    "condition": "DevToolsDataSourceAutoConfiguration.DevToolsDataSourceCondition", 
                    "message": "DevTools DataSource Condition did not find a single DataSource bean"
                }
            ], 
            "matched": [ ]
        }, 
        "RemoteDevToolsAutoConfiguration": {
            "notMatched": [
                {
                    "condition": "OnPropertyCondition", 
                    "message": "@ConditionalOnProperty (spring.devtools.remote.secret) did not find property 'secret'"
                }
            ], 
            "matched": [
                {
                    "condition": "OnClassCondition", 
                    "message": "@ConditionalOnClass found required classes 'javax.servlet.Filter', 'org.springframework.http.server.ServerHttpRequest'; @ConditionalOnMissingClass did not find unwanted class"
                }
            ]
        }
    }
}

heapdump

Returns a GZip compressed JVM heap dump

Start the sample project and visit: http://localhost:8080/actuator/heapdumpA Jvm heap file heapdump will be automatically generated. We can use the Jvm monitoring tool VisualVM that comes with the JDK to open this file to view memory snapshots. Similar to the following picture:

shutdown

Open the interface to gracefully close the Spring Boot application. To use this function, you first need to open it in the configuration file:

management:
  endpoint:
    shutdown:
      enabled: true

After the configuration is complete, start the sample project and use curl to simulate a post request to access the shutdown interface.

The shutdown interface only supports post requests by default.

curl -X POST "http://localhost:8080/actuator/shutdown" 
{
    "message": "Shutting down, bye..."
}

At this point you will find that the application has been closed.

mappings

Describe all URI paths and their mapping to controllers

Start the sample project, visit: http://localhost:8080/actuator/mappingsReturn part of the information is as follows:

{
  "/**/favicon.ico": {
    "bean": "faviconHandlerMapping"
  },
  "{[/hello]}": {
    "bean": "requestMappingHandlerMapping",
    "method": "public java.lang.String com.neo.controller.HelloController.index()"
  },
  "{[/error]}": {
    "bean": "requestMappingHandlerMapping",
    "method": "public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)"
  }
}

threaddump

/threaddump The interface generates a snapshot of current thread activity. This function is very good, and it is convenient for us to check the thread status when we locate the problem every day. It mainly displays information such as thread name, thread ID, thread status, whether to wait for lock resources, etc.

Start the sample project, visit: http://localhost:8080/actuator/threaddumpReturn part of the information is as follows:

[
  {
    "threadName": "http-nio-8088-exec-6",
    "threadId": 49,
    "blockedTime": -1,
    "blockedCount": 0,
    "waitedTime": -1,
    "waitedCount": 2,
    "lockName": "java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@1630a501",
    "lockOwnerId": -1,
    "lockOwnerName": null,
    "inNative": false,
    "suspended": false,
    "threadState": "WAITING",
    "stackTrace": [
      {
        "methodName": "park",
        "fileName": "Unsafe.java",
        "lineNumber": -2,
        "className": "sun.misc.Unsafe",
        "nativeMethod": true
      },
      ...
      {
        "methodName": "run",
        "fileName": "TaskThread.java",
        "lineNumber": 61,
        "className": "org.apache.tomcat.util.threads.TaskThread$WrappingRunnable",
        "nativeMethod": false
      }
      ...
    ],
    "lockInfo": {
      "className": "java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject",
      "identityHashCode": 372286721
    }
  }
  ...
]

When there is a problem in production, you can use the thread snapshot of the application to detect the tasks being executed by the application.

loggers endpoint

Access  http://localhost:8080/actuator/loggers to view information such as the log level of the current application:

This is not special in itself, but there is a very useful function. For example, the log level of our production environment is generally info, but now there is a bug that cannot be checked through the info level, so we can temporarily modify the log level.

For example, the ROOT node in the above figure is at the info level, then we can use tools such as postman to send a post request to modify the log level.

After the modification, you will find that the log changes from the original info to debug:

metrics endpoint

Metrics is a very important monitoring endpoint. Its monitoring content covers some important indicators such as JVM memory, heap, class loading, processor and tomcat container:

You can see that there are a lot of indicators, and you can view the corresponding indicator information by visiting any indicator:

Custom Endpoints

Custom configuration to control whether to enable filtering

actuator:
  filter:
    switch: false

Common annotations for custom monitoring endpoints

Customizing a monitoring endpoint mainly has the following common annotations:

  • @Endpoint : Define a monitoring endpoint, both HTTP and JMX are supported.

  • @WebEndpoint : Define a monitoring endpoint, which only supports HTTP.

  • @JmxEndpoint : Define a monitoring endpoint, only supports JMX mode.

The above three annotations act on the class, indicating that the current class is a monitoring endpoint. In addition, some annotations will be used on methods and parameters:

  • @ReadOperation : acts on the method and can be used to return the information displayed by the endpoint (requested through the Get method).

  • @WriteOperation : acts on the method and can be used to modify the information displayed by the endpoint (requested through the Post method).

  • @DeleteOperation : acts on the method and can be used to delete the corresponding endpoint information (requested through the Delete method).

  • @Selector : Acts on the parameter and is used to locate the specific indicator route of an endpoint.

Under normal circumstances, it is not necessary to customize the Endpoint, but it does not rule out special circumstances. I customize an Endpoint here to put a user object in the request. This user is used for testing and is used to break through the filter below. (to be discussed later), let’s first talk about how to add this user.

The process is as follows:

  • Annotate @Endpointthe corresponding class as an endpoint of Actuator. The annotation needs to specify the id, which is used as the access path, such as here /actuator/super;

  • @ReadOperationTo annotate the query interface, if you want to query according to the path, you need to use @Selectorthe annotation method parameters; note that this place is that @Selector String arg0this arg0 cannot be changed, it can be changed to other, the open interface is still there /{arg0}, which makes your method unable to obtain the parameter value normally .

  • @WriteOperation To annotate the modification interface, note that the request data must be json, and the parameters are not as flexible as in the controller. Entities cannot be used as parameters, and the corresponding attributes in the entities must be used as parameters.

  • When adding a user here, put a user object in the request.

SuperEndPoint :

package com.cff.springbootwork.actuator.endpoint;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.http.HttpServletRequest;

import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.annotation.Selector;
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

@Endpoint(id = "super")
public class SuperEndPoint {
    private Map<String, SuperUser> users = new ConcurrentHashMap<>();

    @ReadOperation
    public Set<String> users() {
        return users.keySet();
    }

    @ReadOperation
    public SuperUser usersIdentify(@Selector String arg0) {
        return users.get(arg0);
    }

    @WriteOperation
    public Set<String> set(String userName, String passwd) {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                .getRequest();
        if (request != null) {
            SuperUser superUser = new SuperUser();
            superUser.setUserName(userName);
            superUser.setPasswd(passwd);
            request.getSession().setAttribute("superUser", superUser);

            users.put(superUser.getUserName(), superUser);
        }

        return users.keySet();
    }

    public static class SuperUser {
        private String userName;
        private String passwd;

        public String getUserName() {
            return userName;
        }

        public void setUserName(String userName) {
            this.userName = userName;
        }

        public String getPasswd() {
            return passwd;
        }

        public void setPasswd(String passwd) {
            this.passwd = passwd;
        }
    }
}

Also register the Endpoint as a bean

MvcEndPointConfig:

package com.cff.springbootwork.actuator;

import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.cff.springbootwork.actuator.endpoint.SuperEndPoint;

@Configuration
@ServletComponentScan 
public class MvcEndPointConfig {

    @Bean
    @ConditionalOnEnabledEndpoint
    public SuperEndPoint superEndPoint() {
        return new SuperEndPoint();
    }
}

Use Filter to restrict access to actuator

As mentioned above, the interface of the actuator needs to be protected. I will use the filter to protect the interface in the simplest way.

  • Filter /actuator/*下all paths, and use actuator.filter.switchattributes to switch filters;

  • If it is /actuator/superthe post operation of the path, let it go, it will put an object in the request;

  • For other /actuator/*下paths, it is necessary to judge whether there is a user object in the request, and if there is no user object, an error message will be returned.

ActuatorPermissionFilter :

package com.cff.springbootwork.actuator.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;

import com.fasterxml.jackson.databind.ObjectMapper;

@WebFilter(urlPatterns = "/actuator/*", filterName = "actuatorPermissionFilter")
@Order(1) // 指定过滤器的执行顺序,值越大越靠后执行
public class ActuatorPermissionFilter implements Filter {
    private String excludePath = "actuator/super";
    @Value("${actuator.filter.switch}")
    Boolean actuatorSwitch;

    @Override
    public void init(FilterConfig filterConfig) {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        if (actuatorSwitch && !(request.getRequestURI().endsWith(excludePath)
                && request.getMethod().equals(HttpMethod.POST.toString()))) {
            Object user = request.getSession().getAttribute("superUser");
            if (user == null) {
                // 未登录,返回数据
                ObjectMapper mapper = new ObjectMapper();
                response.setStatus(HttpStatus.OK.value());
                response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
                mapper.writeValue(response.getWriter(), "您没有权限访问该接口,请使用自定义的登录接口设置superUser后使用!");
                return;
            }
        }
        filterChain.doFilter(servletRequest, servletResponse);

    }

    @Override
    public void destroy() {
    }

}

Spring Boot Monitor as a monitoring page

Additional dependencies

<dependency>
    <groupId>cn.pomit</groupId>
    <artifactId>spring-boot-monitor</artifactId>
    <version>0.0.1</version>
</dependency>

Spring Boot Monitor is a monitoring tool that modifies the Spring boot admin monitoring tool and adapts to a single machine. It perfectly inherits the style of Spring boot admin and directly uses the indicators of the actuator to display.

  • Spring Boot Monitor official website: https://www.pomit.cn/SpringBootMonitor

In the previous maven dependency, it has been stated that it depends on spring-boot-monitor. At this time, no other configuration is required.

Access http://127.0.0.1:8080/monitor, automatically jump to the monitoring page of Spring Boot Monitor.

The monitoring page of Spring Boot Monitor is exactly the same as that of Spring boot admin, and the front-end functions are also exactly the same.

You can see all the indicators of Spring boot at a glance, and you can also perform simple operations.

Of course, if the indicators of the Spring boot actuator are restricted, it will not be able to get the corresponding indicators, because it directly requests the actuator interface.

Source: blog.csdn.net/yunfeather/

article/details/122581536

Guess you like

Origin blog.csdn.net/qq_45635347/article/details/131443687