init
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
package org.dromara.modules.monitor;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* 监控中心
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class RuoYiMonitorApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(RuoYiMonitorApplication.class, args);
|
||||
System.out.println("(♥◠‿◠)ノ゙ 监控中心启动成功 ლ(´ڡ`ლ)゙ ");
|
||||
}
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
package org.dromara.modules.monitor.config;
|
||||
|
||||
import de.codecentric.boot.admin.server.config.EnableAdminServer;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration;
|
||||
import org.springframework.boot.task.ThreadPoolTaskExecutorBuilder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* springboot-admin server配置类
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Configuration
|
||||
@EnableAdminServer
|
||||
public class AdminServerConfig {
|
||||
|
||||
@Lazy
|
||||
@Bean(name = TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME)
|
||||
@ConditionalOnMissingBean(Executor.class)
|
||||
public ThreadPoolTaskExecutor applicationTaskExecutor(ThreadPoolTaskExecutorBuilder builder) {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,54 @@
|
||||
package org.dromara.modules.monitor.config;
|
||||
|
||||
import de.codecentric.boot.admin.server.config.AdminServerProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
|
||||
/**
|
||||
* admin 监控 安全配置
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@EnableWebSecurity
|
||||
@Configuration
|
||||
public class WebSecurityConfigurer {
|
||||
|
||||
private final String adminContextPath;
|
||||
|
||||
public WebSecurityConfigurer(AdminServerProperties adminServerProperties) {
|
||||
this.adminContextPath = adminServerProperties.getContextPath();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
|
||||
SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
|
||||
successHandler.setTargetUrlParameter("redirectTo");
|
||||
successHandler.setDefaultTargetUrl(adminContextPath + "/");
|
||||
|
||||
return httpSecurity
|
||||
.headers((header) ->
|
||||
header.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable))
|
||||
.authorizeHttpRequests((authorize) ->
|
||||
authorize.requestMatchers(
|
||||
new AntPathRequestMatcher(adminContextPath + "/assets/**"),
|
||||
new AntPathRequestMatcher(adminContextPath + "/login")
|
||||
).permitAll()
|
||||
.anyRequest().authenticated())
|
||||
.formLogin((formLogin) ->
|
||||
formLogin.loginPage(adminContextPath + "/login").successHandler(successHandler))
|
||||
.logout((logout) ->
|
||||
logout.logoutUrl(adminContextPath + "/logout"))
|
||||
.httpBasic(Customizer.withDefaults())
|
||||
.csrf(AbstractHttpConfigurer::disable)
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
package org.dromara.modules.monitor.controller;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 对接 prometheus
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/actuator/prometheus")
|
||||
public class PrometheusController {
|
||||
|
||||
@Autowired
|
||||
private DiscoveryClient discoveryClient;
|
||||
|
||||
/**
|
||||
* 从注册中心获取所有服务组装成 prometheus 的数据结构
|
||||
*/
|
||||
@GetMapping("/sd")
|
||||
public List<Map<String, Object>> sd() {
|
||||
List<String> services = discoveryClient.getServices();
|
||||
if (services == null || services.isEmpty()) {
|
||||
return new ArrayList<>(0);
|
||||
}
|
||||
List<Map<String, Object>> list = new ArrayList<>();
|
||||
for (String service : services) {
|
||||
List<ServiceInstance> instances = discoveryClient.getInstances(service);
|
||||
List<String> targets = instances.stream().map(i -> i.getHost() + ":" + i.getPort()).collect(Collectors.toList());
|
||||
|
||||
Map<String, String> labels = new HashMap<>(2);
|
||||
// 数据来源(区分异地使用)
|
||||
// labels.put("__meta_datacenter", "beijing");
|
||||
// 服务名
|
||||
labels.put("__meta_prometheus_job", service);
|
||||
String contextPath = instances.get(0).getMetadata().get("management.context-path");
|
||||
if (contextPath != null) {
|
||||
labels.put("__meta_http_sd_context_path", contextPath.replaceAll("/actuator", ""));
|
||||
}
|
||||
Map<String, Object> group = new HashMap<>(2);
|
||||
group.put("targets", targets);
|
||||
group.put("labels", labels);
|
||||
list.add(group);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 接收 prometheus 报警消息
|
||||
*
|
||||
* @param message 消息体
|
||||
*/
|
||||
@PostMapping("/alerts")
|
||||
public ResponseEntity<Void> alerts(@RequestBody String message) {
|
||||
log.info("[prometheus] alert =>" + message);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,57 @@
|
||||
package org.dromara.modules.monitor.notifier;
|
||||
|
||||
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.stereotype.Component;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import static de.codecentric.boot.admin.server.domain.values.StatusInfo.*;
|
||||
|
||||
/**
|
||||
* 自定义事件通知处理
|
||||
*
|
||||
* @author Lion Li
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class CustomNotifier extends AbstractEventNotifier {
|
||||
|
||||
protected CustomNotifier(InstanceRepository repository) {
|
||||
super(repository);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("all")
|
||||
protected Mono<Void> doNotify(InstanceEvent event, Instance instance) {
|
||||
return Mono.fromRunnable(() -> {
|
||||
// 实例状态改变事件
|
||||
if (event instanceof InstanceStatusChangedEvent) {
|
||||
// 获取实例注册名称
|
||||
String registName = instance.getRegistration().getName();
|
||||
// 获取实例ID
|
||||
String instanceId = event.getInstance().getValue();
|
||||
// 获取实例状态
|
||||
String status = ((InstanceStatusChangedEvent) event).getStatusInfo().getStatus();
|
||||
// 获取服务URL
|
||||
String serviceUrl = instance.getRegistration().getServiceUrl();
|
||||
String statusName = switch (status) {
|
||||
case STATUS_UP -> "服务上线"; // 实例成功启动并可以正常处理请求
|
||||
case STATUS_OFFLINE -> "服务离线"; //实例被手动或自动地从服务中移除
|
||||
case STATUS_RESTRICTED -> "服务受限"; //表示实例在某些方面受限,可能无法完全提供所有服务
|
||||
case STATUS_OUT_OF_SERVICE -> "停止服务状态"; //表示实例已被标记为停止提供服务,可能是计划内维护或测试
|
||||
case STATUS_DOWN -> "服务下线"; //实例因崩溃、错误或其他原因停止运行
|
||||
case STATUS_UNKNOWN -> "服务未知异常"; //监控系统无法确定实例的当前状态
|
||||
default -> "未知状态"; //没有匹配的状态
|
||||
};
|
||||
log.info("Instance Status Change: 状态名称【{}】, 注册名称【{}】, 实例ID【{}】, 状态【{}】, 服务URL【{}】",
|
||||
statusName, registName, instanceId, status, serviceUrl);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
# Tomcat
|
||||
server:
|
||||
port: 9100
|
||||
|
||||
# Spring
|
||||
spring:
|
||||
application:
|
||||
# 应用名称
|
||||
name: ruoyi-monitor
|
||||
profiles:
|
||||
# 环境配置
|
||||
active: @profiles.active@
|
||||
|
||||
--- # nacos 配置
|
||||
spring:
|
||||
cloud:
|
||||
nacos:
|
||||
# nacos 服务地址
|
||||
server-addr: @nacos.server@
|
||||
username: @nacos.username@
|
||||
password: @nacos.password@
|
||||
discovery:
|
||||
# 注册组
|
||||
group: @nacos.discovery.group@
|
||||
namespace: ${spring.profiles.active}
|
||||
config:
|
||||
# 配置组
|
||||
group: @nacos.config.group@
|
||||
namespace: ${spring.profiles.active}
|
||||
config:
|
||||
import:
|
||||
- optional:nacos:application-common.yml
|
||||
- optional:nacos:${spring.application.name}.yml
|
10
ruoyi-visual/ruoyi-monitor/src/main/resources/banner.txt
Normal file
10
ruoyi-visual/ruoyi-monitor/src/main/resources/banner.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
Spring Boot Version: ${spring-boot.version}
|
||||
Spring Application Name: ${spring.application.name}
|
||||
_ _ _
|
||||
(_) (_)| |
|
||||
_ __ _ _ ___ _ _ _ ______ _ __ ___ ___ _ __ _ | |_ ___ _ __
|
||||
| '__|| | | | / _ \ | | | || ||______|| '_ ` _ \ / _ \ | '_ \ | || __| / _ \ | '__|
|
||||
| | | |_| || (_) || |_| || | | | | | | || (_) || | | || || |_ | (_) || |
|
||||
|_| \__,_| \___/ \__, ||_| |_| |_| |_| \___/ |_| |_||_| \__| \___/ |_|
|
||||
__/ |
|
||||
|___/
|
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration scan="true" scanPeriod="60 seconds" debug="false">
|
||||
<!-- 日志存放路径 -->
|
||||
<property name="log.path" value="logs/${project.artifactId}" />
|
||||
<!-- 日志输出格式 -->
|
||||
<property name="console.log.pattern"
|
||||
value="%cyan(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/>
|
||||
|
||||
<!-- 控制台输出 -->
|
||||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>${console.log.pattern}</pattern>
|
||||
<charset>utf-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<include resource="logback-common.xml" />
|
||||
|
||||
<include resource="logback-logstash.xml" />
|
||||
|
||||
<!-- 开启 skywalking 日志收集 -->
|
||||
<include resource="logback-skylog.xml" />
|
||||
|
||||
<!--系统操作日志-->
|
||||
<root level="info">
|
||||
<appender-ref ref="console" />
|
||||
</root>
|
||||
</configuration>
|
Reference in New Issue
Block a user