SpringBoot admin+Eureka + nail notification

Keywords: Programming Spring Maven SDK Apache

I. Effect

Login account + password

Monitoring service

View live logs

Nail notification

2. What is Spring Boot Admin?

Spring Boot Admin is an open source community project for managing and monitoring spring boot applications. The application, as the Spring Boot Admin Client, registers with the Spring Boot Admin Server (via HTTP) or uses the spring cloud Registry (e.g. Eureka, Consul) to discover. The UI is the Vue.js application, which shows some monitoring on the Actuator endpoint of Spring Boot Admin Client. The server adopts the mode of Spring WebFlux + Netty. Spring Boot Admin provides the following functions for registered applications:

  • Show health

  • Show details, such as

  • JVM and memory metrics

  • micrometer.io index

  • Data source indicators

  • Cache index

  • Show build information number

  • Follow and download log files

  • View the jvm system - and environment properties

  • View Spring Boot configuration properties

  • postable / env - and / refresh endpoint supporting Spring Cloud

  • Easy log level management

  • Interact with JMX beans

  • View thread dump

  • View HTTP traces

  • View auditevents

  • View HTTP endpoints

  • View scheduled tasks

  • View and delete active sessions (using spring session)

  • View Flyway / Liquibase database migration

  • Download heapdump

  • Status change notification (via email, Slack, Hipchat,...)

  • Event log for state changes (non persistent)

Three, principle

Using spring boot activator to monitor applications

IV. integrated Eureka Registration Center

1. Create Eureka server and google by yourself

2. Create spring boot admin

This is a Spring Boot Admin Server.

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 http://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.1.6.RELEASE</version>
        <relativepath /> <!-- lookup parent from repository -->
    </parent>
    <packaging>jar</packaging>
    <artifactid>spring-boot-admin</artifactid>
    <name>spring-boot-admin</name>
    <description>Spring Boot Admin Server end</description>

     <properties>
        <java.version>1.8</java.version>
        <spring-boot-admin.version>2.1.6</spring-boot-admin.version>
        <spring-cloud.version>Greenwich.RELEASE</spring-cloud.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.cloud</groupid>
            <artifactid>spring-cloud-starter-netflix-eureka-client</artifactid>
        </dependency>

        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-test</artifactid>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-security</artifactid>
        </dependency>
        <dependency>
            <groupid>org.jolokia</groupid>
            <artifactid>jolokia-core</artifactid>
        </dependency>
    </dependencies>

    <dependencymanagement>
        <dependencies>
            <dependency>
                <groupid>org.springframework.cloud</groupid>
                <artifactid>spring-cloud-dependencies</artifactid>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <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>
            </plugin>
        </plugins>
    </build>

</project>

application.yml

spring:
  application:
    name: admin-server
server:
  port: 1300
eureka:
  client:
    registryFetchIntervalSeconds: 5
    service-url:
      defaultZone: ${EUREKA_SERVICE_URL:http://localhost:8761}/eureka/
  instance:
    leaseRenewalIntervalInSeconds: 10
    health-check-url-path: /actuator/health

management:
  endpoints:
    web:
      exposure:
        include: "*"
  endpoint:
    health:
      show-details: ALWAYS

Start class SpringbootAdminServerApplication

@SpringBootApplication
@EnableAdminServer
@EnableEurekaClient
public class ScAdminServerApplication {

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

}

3. Monitored end

The monitored end needs to release the end point

application.yml

spring:
  application:
    name: admin-client
eureka:
  instance:
    leaseRenewalIntervalInSeconds: 10
    health-check-url-path: /actuator/health

  client:
    registryFetchIntervalSeconds: 5
    service-url:
      defaultZone: ${EUREKA_SERVICE_URL:http://localhost:8761}/eureka/
management:
  endpoints:
    web:
      exposure:
        include: "*"
  endpoint:
    health:
      show-details: ALWAYS
server:
  port: 8762

admin will pull the registration information from Eureka and take the initiative to register.

V. integrating Spring Security

There are many ways to authenticate and authorize in a Web application, so Spring Boot Admin does not provide a default method. By default, Spring Boot Admin server UI provides a login page and a logout button. We use Spring Security to implement the security authentication that requires user name and password login.

The following dependencies need to be added to the pom file of the springboot admin project:

<dependency>
    <groupid>org.springframework.boot</groupid>
    <artifactid>spring-boot-starter-security</artifactid>
</dependency>

Configure the spring security user name and password in the application.yml configuration file of the spingboot admin worker. At this time, you need to bring the metadata map information when registering the service, as follows:

spring:
  security:
    user:
      name: "admin"
      password: "admin"
      
eureka:
  instance:
    metadata-map:
      user.name: ${spring.security.user.name}
      user.password: ${spring.security.user.password}
      startup: ${random.int}    #needed to trigger info and endpoint update after restart

Write a configuration class SecuritySecureConfig to inherit WebSecurityConfigurerAdapter. The configuration is as follows:

/**
 * security To configure
 * @author wangjiafang
 * @date 2019/10/10
 */
@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");
        successHandler.setDefaultTargetUrl(adminContextPath + "/");

        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()
                .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
                .ignoringRequestMatchers(
                        new AntPathRequestMatcher(adminContextPath + "/instances", HttpMethod.POST.toString()),
                        new AntPathRequestMatcher(adminContextPath + "/instances/*", HttpMethod.DELETE.toString()),
                        new AntPathRequestMatcher(adminContextPath + "/actuator/**")
                );
        // @formatter:on
    }
}

If you visit http:localhost:1300 again, the login interface will appear. The password is configured in the configuration file. The account admin password is admin.

Six. Notice

Custom notification + nail notification

1. Create a nail robot, get the token, how to create a nail robot, please google
2. Download sdk

Nailing officially provides a unified SDK. Using the SDK, you can easily call the server API, but it is not put in the public maven warehouse. You need to make your own download Then import it to the project or upload it to your built nexus private server

3. Custom Notifier

By extending AbstractEventNotifier or AbstractStatusChangeNotifier. Write a custom notifier in the springboot admin server project:


import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiRobotSendRequest;
import com.taobao.api.ApiException;
import de.codecentric.boot.admin.server.domain.entities.Instance;
import de.codecentric.boot.admin.server.domain.entities.InstanceRepository;
import de.codecentric.boot.admin.server.domain.events.InstanceEvent;
import de.codecentric.boot.admin.server.domain.events.InstanceStatusChangedEvent;
import de.codecentric.boot.admin.server.notify.AbstractEventNotifier;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;


/**
 * Nail notification
 * @author wangjiafang
 * @date 2019/10/10
 */
@Component
@Slf4j
public class CustomNotifier  extends AbstractEventNotifier {

    /**
     * Message template
     */
    private static final String template = "service name:%s(%s) \n state:%s(%s) \n service ip:%s";

    @Value("${spring.admin.ding-talk-token}")
    private String dingTalkToken;


    public CustomNotifier(InstanceRepository repository) {
        super(repository);
    }

    @Override
    protected Mono<void> doNotify(InstanceEvent event, Instance instance) {
        return Mono.fromRunnable(() -&gt; {
            if (event instanceof InstanceStatusChangedEvent) {
                log.info("Instance {} ({}) is {}", instance.getRegistration().getName(), event.getInstance(),
                        ((InstanceStatusChangedEvent) event).getStatusInfo().getStatus());


                String status = ((InstanceStatusChangedEvent) event).getStatusInfo().getStatus();
                String messageText = null;
                switch (status) {
                    // Health examination failed
                    case "DOWN":
                        log.info("Send the notice of failing health examination!");
                        messageText = String.format(template, instance.getRegistration().getName(),event.getInstance(), ((InstanceStatusChangedEvent) event).getStatusInfo().getStatus(),"Health examination failed",instance.getRegistration().getServiceUrl());
                        this.sendMessage(messageText);
                        break;
                    // Service offline
                    case "OFFLINE":
                        log.info("Send service offline notification!");
                        messageText = String.format(template, instance.getRegistration().getName(),event.getInstance(), ((InstanceStatusChangedEvent) event).getStatusInfo().getStatus(),"Service offline",instance.getRegistration().getServiceUrl());
                        this.sendMessage(messageText);
                        break;
                    //Service on-line
                    case "UP":
                        log.info("Send service online notice!");
                        messageText = String.format(template, instance.getRegistration().getName(),event.getInstance(), ((InstanceStatusChangedEvent) event).getStatusInfo().getStatus(),"Service on-line",instance.getRegistration().getServiceUrl());
                        this.sendMessage(messageText);
                        break;
                    // Service unknown exception
                    case "UNKNOWN":
                        log.info("Send service unknown exception notification!");
                        messageText = String.format(template, instance.getRegistration().getName(),event.getInstance(), ((InstanceStatusChangedEvent) event).getStatusInfo().getStatus(),"Service unknown exception",instance.getRegistration().getServiceUrl());
                        this.sendMessage(messageText);
                        break;
                    default:
                        break;
                }
            } else {
                log.info("Instance {} ({}) {}", instance.getRegistration().getName(), event.getInstance(),
                    event.getType());
            }
        });
    }

    /**
     * send message
     * @param messageText
     */
    private void sendMessage(String messageText){
        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/robot/send?access_token="+dingTalkToken);
        OapiRobotSendRequest request = new OapiRobotSendRequest();

        request.setMsgtype("text");
        OapiRobotSendRequest.Text text = new OapiRobotSendRequest.Text();
        text.setContent(messageText);
        request.setText(text);

        try {
            client.execute(request);
        } catch (ApiException e) {
            log.info("[ERROR] sendMessage", e);
        }
    }
}

7. View real-time logs

To view the real-time log in the springbootadmin panel, you need to specify the log output address in the project. For example, my log is under / logs / project name / project name - info.log.

logging:
  file: /logs/${spring.application.name}/${spring.application.name}-info.log

Posted by warpdesign on Thu, 17 Oct 2019 20:58:42 -0700