diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/pom.xml b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/pom.xml
index 40c0198a..a05c33f3 100644
--- a/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/pom.xml
+++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-boot-2x-plugin/pom.xml
@@ -38,11 +38,19 @@
provided
+
+ cn.hippo4j
+ hippo4j-threadpool-core
+ ${project.version}
+ provided
+
+
com.ctrip.framework.apollo
apollo-client
provided
+
com.alibaba.nacos
nacos-client
diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/pom.xml b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/pom.xml
index c3780048..c472c7b3 100644
--- a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/pom.xml
+++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/pom.xml
@@ -49,5 +49,30 @@
${project.version}
provided
+
+ cn.hippo4j
+ hippo4j-threadpool-adapter-web
+ ${project.version}
+ provided
+
+
+ cn.hippo4j
+ hippo4j-threadpool-monitor-elasticsearch
+ ${project.version}
+
+
+ cn.hippo4j
+ hippo4j-threadpool-monitor-local-log
+ ${project.version}
+
+
+ cn.hippo4j
+ hippo4j-threadpool-monitor-micrometer
+ ${project.version}
+
+
+ io.micrometer
+ micrometer-registry-prometheus
+
\ No newline at end of file
diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/alarm/AgentModeNotifyConfigBuilder.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/alarm/AgentModeNotifyConfigBuilder.java
deleted file mode 100644
index d1345334..00000000
--- a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/alarm/AgentModeNotifyConfigBuilder.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package cn.hippo4j.agent.plugin.spring.common.alarm;
-
-import cn.hippo4j.agent.plugin.spring.common.support.ThreadPoolCheckAlarmSupport;
-import cn.hippo4j.common.api.IExecutorProperties;
-import cn.hippo4j.common.model.executor.ExecutorNotifyProperties;
-import cn.hippo4j.common.model.executor.ExecutorProperties;
-import cn.hippo4j.common.toolkit.CollectionUtil;
-import cn.hippo4j.common.toolkit.StringUtil;
-import cn.hippo4j.threadpool.dynamic.mode.config.properties.NotifyPlatformProperties;
-import cn.hippo4j.threadpool.message.api.NotifyConfigBuilder;
-import cn.hippo4j.threadpool.message.api.NotifyConfigDTO;
-import cn.hippo4j.threadpool.message.core.service.AlarmControlHandler;
-import lombok.AllArgsConstructor;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.stream.Collectors;
-
-import static cn.hippo4j.agent.plugin.spring.common.support.SpringPropertiesLoader.BOOTSTRAP_CONFIG_PROPERTIES;
-import static cn.hippo4j.common.constant.Constants.DEFAULT_INTERVAL;
-
-/**
- * This class is responsible for building the notification configurations for thread pools in an agent mode.
- * It implements the {@link NotifyConfigBuilder} interface and provides methods to build and initialize
- * notification configurations for various platforms and types (e.g., ALARM, CONFIG).
- *
- *
The configuration is based on the properties loaded from the bootstrap configuration and includes
- * handling for alarm control and notification intervals.
- */
-@AllArgsConstructor
-public class AgentModeNotifyConfigBuilder implements NotifyConfigBuilder {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(ThreadPoolCheckAlarmSupport.class);
-
- private final AlarmControlHandler alarmControlHandler;
-
- /**
- * Builds the notification configurations for all executors defined in the bootstrap configuration.
- *
- * This method filters the executors based on their alarm settings and constructs the notification
- * configurations accordingly. If global alarm settings are disabled and there are no specific alarms
- * configured for any executor, the method returns an empty map.
- *
- * @return A map containing the notification configurations, keyed by the notification type (e.g., ALARM, CONFIG).
- */
- public Map> buildNotify() {
- Map> resultMap = new HashMap<>();
- boolean globalAlarm = Optional.ofNullable(BOOTSTRAP_CONFIG_PROPERTIES.getDefaultExecutor())
- .map(ExecutorProperties::getAlarm)
- .orElse(true);
- List executors = BOOTSTRAP_CONFIG_PROPERTIES.getExecutors();
- if (CollectionUtil.isEmpty(executors)) {
- LOGGER.warn("Failed to build notify, executors configuration is empty.");
- return resultMap;
- }
- List actual = executors.stream()
- .filter(each -> Optional.ofNullable(each.getAlarm())
- .orElse(false))
- .collect(Collectors.toList());
- if (!globalAlarm && CollectionUtil.isEmpty(actual)) {
- return resultMap;
- }
- for (ExecutorProperties executorProperties : executors) {
- Map> buildSingleNotifyConfig = buildSingleNotifyConfig(executorProperties);
- initCacheAndLock(buildSingleNotifyConfig);
- resultMap.putAll(buildSingleNotifyConfig);
- }
-
- return resultMap;
- }
-
- /**
- * Builds the notification configurations for a single executor.
- *
- * This method generates two types of notifications: ALARM and CONFIG. For each type, it creates
- * notification configurations based on the platforms defined in the bootstrap configuration.
- *
- * @param executorProperties The properties of the executor for which to build the notification configurations.
- * @return A map containing the notification configurations for the given executor, keyed by the notification type.
- */
- public Map> buildSingleNotifyConfig(IExecutorProperties executorProperties) {
- String threadPoolId = executorProperties.getThreadPoolId();
- Map> resultMap = new HashMap<>();
- String alarmBuildKey = threadPoolId + "+ALARM";
- List alarmNotifyConfigs = new ArrayList<>();
- List notifyPlatforms = BOOTSTRAP_CONFIG_PROPERTIES.getNotifyPlatforms();
- for (NotifyPlatformProperties platformProperties : notifyPlatforms) {
- NotifyConfigDTO notifyConfig = new NotifyConfigDTO();
- notifyConfig.setPlatform(platformProperties.getPlatform());
- notifyConfig.setTpId(threadPoolId);
- notifyConfig.setType("ALARM");
- notifyConfig.setSecret(platformProperties.getSecret());
- notifyConfig.setSecretKey(getToken(platformProperties));
- notifyConfig.setInterval(buildInterval(executorProperties));
- notifyConfig.setReceives(buildReceive(executorProperties));
- alarmNotifyConfigs.add(notifyConfig);
- }
- resultMap.put(alarmBuildKey, alarmNotifyConfigs);
- String changeBuildKey = threadPoolId + "+CONFIG";
- List changeNotifyConfigs = new ArrayList<>();
- for (NotifyPlatformProperties platformProperties : notifyPlatforms) {
- NotifyConfigDTO notifyConfig = new NotifyConfigDTO();
- notifyConfig.setPlatform(platformProperties.getPlatform());
- notifyConfig.setTpId(threadPoolId);
- notifyConfig.setType("CONFIG");
- notifyConfig.setSecretKey(getToken(platformProperties));
- notifyConfig.setSecret(platformProperties.getSecret());
- notifyConfig.setReceives(buildReceive(executorProperties));
- changeNotifyConfigs.add(notifyConfig);
- }
- resultMap.put(changeBuildKey, changeNotifyConfigs);
- return resultMap;
- }
-
- /**
- * Retrieves the token for the given notification platform properties.
- *
- * If the token is not explicitly set, the method returns the secret key as the fallback.
- *
- * @param platformProperties The platform properties from which to retrieve the token.
- * @return The token or secret key associated with the given platform properties.
- */
- private String getToken(NotifyPlatformProperties platformProperties) {
- return StringUtil.isNotBlank(platformProperties.getToken()) ? platformProperties.getToken() : platformProperties.getSecretKey();
- }
-
- /**
- * Builds the notification interval for the given executor properties.
- *
- * This method first checks the executor's specific notify configuration. If not set, it falls back
- * to the default executor configuration in the bootstrap properties.
- *
- * @param executorProperties The properties of the executor for which to build the notification interval.
- * @return The notification interval in seconds.
- */
- private int buildInterval(IExecutorProperties executorProperties) {
- return Optional.ofNullable(executorProperties.getNotify())
- .map(ExecutorNotifyProperties::getInterval)
- .orElse(Optional.ofNullable(BOOTSTRAP_CONFIG_PROPERTIES.getDefaultExecutor())
- .map(ExecutorProperties::getNotify)
- .map(ExecutorNotifyProperties::getInterval)
- .orElse(DEFAULT_INTERVAL));
- }
-
- /**
- * Builds the notification recipients for the given executor properties.
- *
- * This method first checks the executor's specific notify configuration. If not set, it falls back
- * to the default executor configuration in the bootstrap properties.
- *
- * @param executorProperties The properties of the executor for which to build the notification recipients.
- * @return A string containing the recipients of the notifications.
- */
- private String buildReceive(IExecutorProperties executorProperties) {
- return Optional.ofNullable(executorProperties.getNotify())
- .map(ExecutorNotifyProperties::getReceives)
- .orElse(Optional.ofNullable(BOOTSTRAP_CONFIG_PROPERTIES.getDefaultExecutor())
- .map(ExecutorProperties::getNotify)
- .map(ExecutorNotifyProperties::getReceives).orElse(""));
- }
-
- /**
- * Initializes the cache and lock mechanisms for the given notification configurations.
- *
- * This method is primarily responsible for setting up alarm controls based on the notification
- * configurations, ensuring that the appropriate cache and lock mechanisms are initialized for
- * each thread pool and platform combination.
- *
- * @param buildSingleNotifyConfig A map containing the notification configurations that need cache and lock initialization.
- */
- public void initCacheAndLock(Map> buildSingleNotifyConfig) {
- buildSingleNotifyConfig.forEach(
- (key, val) -> val.stream()
- .filter(each -> Objects.equals("ALARM", each.getType()))
- .forEach(each -> alarmControlHandler.initCacheAndLock(each.getTpId(), each.getPlatform(), each.getInterval())));
- }
-}
diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/conf/SpringBootConfig.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/conf/SpringBootConfig.java
index b93149a2..48e05894 100644
--- a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/conf/SpringBootConfig.java
+++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/conf/SpringBootConfig.java
@@ -30,11 +30,13 @@ public class SpringBootConfig {
/**
* Spring
*/
+ @SpringBootConfigNode(root = SpringBootConfig.class)
public static class Spring {
/**
* Dynamic
*/
+ @SpringBootConfigNode(root = SpringBootConfig.class)
public static class Dynamic {
/**
@@ -79,6 +81,9 @@ public class SpringBootConfig {
public static Long initialDelay = 10000L;
public static Long collectInterval = 5000L;
+
+ public static Integer AGENT_MICROMETER_PORT;
+
}
public static String CONFIG_FILE_TYPE;
diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/monitor/MonitorHandlersConfigurator.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/monitor/MonitorHandlersConfigurator.java
new file mode 100644
index 00000000..56e1b7f0
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/monitor/MonitorHandlersConfigurator.java
@@ -0,0 +1,186 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.plugin.spring.common.monitor;
+
+import cn.hippo4j.common.extension.spi.ServiceLoaderRegistry;
+import cn.hippo4j.common.logging.api.ILog;
+import cn.hippo4j.common.logging.api.LogManager;
+import cn.hippo4j.common.monitor.MonitorCollectTypeEnum;
+import cn.hippo4j.common.monitor.MonitorHandlerTypeEnum;
+import cn.hippo4j.core.executor.state.ThreadPoolRunStateHandler;
+import cn.hippo4j.core.toolkit.inet.InetUtils;
+import cn.hippo4j.core.toolkit.inet.InetUtilsProperties;
+import cn.hippo4j.monitor.elasticsearch.AdapterThreadPoolElasticSearchMonitorHandler;
+import cn.hippo4j.monitor.elasticsearch.DynamicThreadPoolElasticSearchMonitorHandler;
+import cn.hippo4j.monitor.elasticsearch.WebThreadPoolElasticSearchMonitorHandler;
+import cn.hippo4j.monitor.local.log.AdapterThreadPoolLocalLogMonitorHandler;
+import cn.hippo4j.monitor.local.log.DynamicThreadPoolLocalLogMonitorHandler;
+import cn.hippo4j.monitor.local.log.WebThreadPoolLocalLogMonitorHandler;
+import cn.hippo4j.monitor.micrometer.AdapterThreadPoolMicrometerMonitorHandler;
+import cn.hippo4j.monitor.micrometer.DynamicThreadPoolMicrometerMonitorHandler;
+import cn.hippo4j.monitor.micrometer.WebThreadPoolMicrometerMonitorHandler;
+import cn.hippo4j.threadpool.dynamic.mode.config.properties.MonitorProperties;
+import cn.hippo4j.threadpool.monitor.api.ThreadPoolMonitor;
+import org.springframework.core.env.ConfigurableEnvironment;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+
+import static cn.hippo4j.agent.plugin.spring.common.support.SpringPropertiesLoader.INET_UTILS_PROPERTIES;
+
+/**
+ * This class is responsible for configuring and initializing monitoring handlers
+ * for various types of thread pools. It maps specific monitoring types (e.g., Micrometer,
+ * Log, Elasticsearch) to their corresponding handler initializers and manages the
+ * setup process based on the provided configuration.
+ */
+public class MonitorHandlersConfigurator {
+
+ private static final ILog LOGGER = LogManager.getLogger(MonitorHandlersConfigurator.class);
+
+ // Maps thread pool types to their corresponding handler constructors
+ private static final Map> handlerMap = new HashMap<>();
+
+ static {
+ // Initialize the handler map with specific monitoring types
+ handlerMap.put(MonitorCollectTypeEnum.MICROMETER.getValue(), MonitorHandlersConfigurator::handleMicrometer);
+ handlerMap.put(MonitorCollectTypeEnum.LOG.getValue(), MonitorHandlersConfigurator::handleLog);
+ handlerMap.put(MonitorCollectTypeEnum.ELASTICSEARCH.getValue(), MonitorHandlersConfigurator::handleElasticSearch);
+ }
+
+ /**
+ * Initializes the monitoring handlers based on the provided monitoring configuration.
+ *
+ * This method performs the following tasks:
+ *
+ * - Parses the configured monitoring types and thread pool types.
+ * - Initializes a monitoring context with the necessary thread pool monitors and state handler.
+ * - For each configured monitoring type, invokes the corresponding handler initializer
+ * for each relevant thread pool type.
+ * - Logs a warning if an unrecognized monitoring type is encountered.
+ * - Registers and adds thread pool monitors that match the configured monitoring types.
+ *
+ *
+ * @param monitor The monitoring properties configuration.
+ * @param environment The application environment from which additional configuration can be loaded.
+ * @param threadPoolMonitors A list to hold the initialized thread pool monitors.
+ */
+ public static void initializeMonitorHandlers(MonitorProperties monitor, ConfigurableEnvironment environment, List threadPoolMonitors) {
+ List collectTypes = Arrays.asList(monitor.getCollectTypes().split(","));
+ List threadPoolTypes = Arrays.asList(monitor.getThreadPoolTypes().split(","));
+ ThreadPoolRunStateHandler threadPoolRunStateHandler = new ThreadPoolRunStateHandler(
+ new InetUtils(INET_UTILS_PROPERTIES), environment);
+
+ MonitorHandlerContext context = new MonitorHandlerContext(threadPoolMonitors, threadPoolRunStateHandler);
+
+ // Initialize handlers for each configured monitoring type and thread pool type
+ for (String collectType : collectTypes) {
+ if (handlerMap.containsKey(collectType)) {
+ for (MonitorHandlerTypeEnum type : MonitorHandlerTypeEnum.values()) {
+ if (threadPoolTypes.contains(type.name().toLowerCase())) {
+ handlerMap.get(collectType).accept(type, context);
+ }
+ }
+ } else {
+ LOGGER.warn("[Hippo4j-Agent] MonitorConfigurator initialize Unrecognized collect type: [{}]", collectType);
+ }
+ }
+
+ // Register and add dynamic thread pool monitors matching the configured types
+ Collection dynamicThreadPoolMonitors = ServiceLoaderRegistry.getSingletonServiceInstances(ThreadPoolMonitor.class);
+ dynamicThreadPoolMonitors.stream().filter(each -> collectTypes.contains(each.getType())).forEach(threadPoolMonitors::add);
+ }
+
+ /**
+ * Initializes Micrometer-based monitoring handlers for the specified thread pool type.
+ *
+ * @param type The type of thread pool to be monitored.
+ * @param context The context containing the monitors and state handler.
+ */
+ private static void handleMicrometer(MonitorHandlerTypeEnum type, MonitorHandlerContext context) {
+ switch (type) {
+ case DYNAMIC:
+ context.monitors.add(new DynamicThreadPoolMicrometerMonitorHandler(context.threadPoolRunStateHandler));
+ break;
+ case WEB:
+ context.monitors.add(new WebThreadPoolMicrometerMonitorHandler());
+ break;
+ case ADAPTER:
+ context.monitors.add(new AdapterThreadPoolMicrometerMonitorHandler());
+ break;
+ }
+ }
+
+ /**
+ * Initializes Log-based monitoring handlers for the specified thread pool type.
+ *
+ * @param type The type of thread pool to be monitored.
+ * @param context The context containing the monitors and state handler.
+ */
+ private static void handleLog(MonitorHandlerTypeEnum type, MonitorHandlerContext context) {
+ switch (type) {
+ case DYNAMIC:
+ context.monitors.add(new DynamicThreadPoolLocalLogMonitorHandler(context.threadPoolRunStateHandler));
+ break;
+ case WEB:
+ context.monitors.add(new WebThreadPoolLocalLogMonitorHandler());
+ break;
+ case ADAPTER:
+ context.monitors.add(new AdapterThreadPoolLocalLogMonitorHandler());
+ break;
+ }
+ }
+
+ /**
+ * Initializes Elasticsearch-based monitoring handlers for the specified thread pool type.
+ *
+ * @param type The type of thread pool to be monitored.
+ * @param context The context containing the monitors and state handler.
+ */
+ private static void handleElasticSearch(MonitorHandlerTypeEnum type, MonitorHandlerContext context) {
+ switch (type) {
+ case DYNAMIC:
+ context.monitors.add(new DynamicThreadPoolElasticSearchMonitorHandler(context.threadPoolRunStateHandler));
+ break;
+ case WEB:
+ context.monitors.add(new WebThreadPoolElasticSearchMonitorHandler(context.threadPoolRunStateHandler));
+ break;
+ case ADAPTER:
+ context.monitors.add(new AdapterThreadPoolElasticSearchMonitorHandler(context.threadPoolRunStateHandler));
+ break;
+ }
+ }
+
+ /**
+ * A helper class to manage the context in which monitoring handlers are initialized.
+ */
+ private static class MonitorHandlerContext {
+
+ List monitors;
+ ThreadPoolRunStateHandler threadPoolRunStateHandler;
+
+ MonitorHandlerContext(List monitors, ThreadPoolRunStateHandler handler) {
+ this.monitors = monitors;
+ this.threadPoolRunStateHandler = handler;
+ }
+ }
+}
diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/monitor/MonitorMetricEndpoint.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/monitor/MonitorMetricEndpoint.java
new file mode 100644
index 00000000..b8ae07bc
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/monitor/MonitorMetricEndpoint.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.plugin.spring.common.monitor;
+
+import cn.hippo4j.agent.plugin.spring.common.conf.SpringBootConfig;
+import cn.hippo4j.common.logging.api.ILog;
+import cn.hippo4j.common.logging.api.LogManager;
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+import com.sun.net.httpserver.HttpServer;
+import io.micrometer.core.instrument.Metrics;
+import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
+import io.micrometer.prometheus.PrometheusConfig;
+import io.micrometer.prometheus.PrometheusMeterRegistry;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+
+/**
+ * This class is responsible for exposing Prometheus metrics via an HTTP endpoint.
+ * It initializes the Prometheus registry, binds it to the global metrics registry,
+ * and starts an HTTP server to serve the metrics data.
+ */
+public class MonitorMetricEndpoint {
+
+ private static final ILog LOGGER = LogManager.getLogger(MonitorHandlersConfigurator.class);
+
+ /**
+ * Starts the Prometheus metrics HTTP server.
+ *
+ * This method performs the following steps:
+ *
+ * - Initializes the PrometheusMeterRegistry with the default configuration.
+ * - Binds the Prometheus registry to the global CompositeMeterRegistry.
+ * - Attempts to start an HTTP server on the configured port to expose the Prometheus metrics.
+ *
+ * If the port is not configured, or if there is an error starting the server, appropriate error messages
+ * are logged, and the method returns without starting the server.
+ *
+ */
+ public static void startPrometheusEndpoint() {
+
+ // Initialize the Prometheus registry
+ PrometheusMeterRegistry prometheusRegistry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);
+
+ // Bind the Prometheus registry to the global Metrics registry
+ CompositeMeterRegistry globalRegistry = Metrics.globalRegistry;
+ globalRegistry.add(prometheusRegistry);
+
+ // Get the configured port for the Prometheus metrics HTTP server
+ Integer port = SpringBootConfig.Spring.Dynamic.Thread_Pool.Monitor.AGENT_MICROMETER_PORT;
+ if (port == null) {
+ LOGGER.error(
+ "[Hippo4j-Agent] Failed to start Prometheus metrics endpoint server. Please configure the exposed endpoint by adding: spring.dynamic.thread-pool.monitor.port=xxx to the configuration file");
+ return;
+ }
+
+ // Create the HTTP server
+ HttpServer server = null;
+ try {
+ server = HttpServer.create(new InetSocketAddress(port), 0);
+ } catch (IOException e) {
+ LOGGER.error("[Hippo4j-Agent] Failed to start Prometheus metrics endpoint server", e);
+ return;
+ }
+
+ // Register the /actuator/prometheus context to handle metrics requests
+ server.createContext("/actuator/prometheus", exchange -> {
+ String response = prometheusRegistry.scrape(); // Get metrics data in Prometheus format
+ exchange.sendResponseHeaders(200, response.getBytes().length);
+ try (OutputStream os = exchange.getResponseBody()) {
+ os.write(response.getBytes());
+ }
+ });
+
+ // Start the server
+ server.start();
+ LOGGER.info("[Hippo4j-Agent] Prometheus metrics server started on port {}", port);
+ }
+
+}
diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/SpringPropertiesLoader.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/SpringPropertiesLoader.java
index bb6e521f..d9fbfac7 100644
--- a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/SpringPropertiesLoader.java
+++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/SpringPropertiesLoader.java
@@ -21,6 +21,7 @@ import cn.hippo4j.agent.core.boot.SpringBootConfigInitializer;
import cn.hippo4j.agent.plugin.spring.common.toolkit.SpringPropertyBinder;
import cn.hippo4j.common.logging.api.ILog;
import cn.hippo4j.common.logging.api.LogManager;
+import cn.hippo4j.core.toolkit.inet.InetUtilsProperties;
import cn.hippo4j.threadpool.dynamic.mode.config.properties.BootstrapConfigProperties;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.EnumerablePropertySource;
@@ -42,6 +43,8 @@ public class SpringPropertiesLoader {
public static BootstrapConfigProperties BOOTSTRAP_CONFIG_PROPERTIES = new BootstrapConfigProperties();
+ public static InetUtilsProperties INET_UTILS_PROPERTIES = new InetUtilsProperties();
+
public static void loadSpringProperties(ConfigurableEnvironment environment) {
Iterator> iterator = environment.getPropertySources().iterator();
Properties properties = new Properties();
@@ -83,8 +86,11 @@ public class SpringPropertiesLoader {
environment.getPropertySources().addFirst(propertySource);
// initialize BootstrapConfigProperties
BOOTSTRAP_CONFIG_PROPERTIES = SpringPropertyBinder.bindProperties(environment, PREFIX, BootstrapConfigProperties.class);
-
+ INET_UTILS_PROPERTIES = SpringPropertyBinder.bindProperties(environment, InetUtilsProperties.PREFIX, InetUtilsProperties.class);
+ // Enable the thread pool check alert handler
ThreadPoolCheckAlarmSupport.enableThreadPoolCheckAlarmHandler();
+ // Enable thread pool monitor handler
+ ThreadPoolMonitorSupport.enableThreadPoolMonitorHandler(environment);
}
}
diff --git a/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/ThreadPoolMonitorSupport.java b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/ThreadPoolMonitorSupport.java
new file mode 100644
index 00000000..3af1e1e6
--- /dev/null
+++ b/agent/hippo4j-agent-plugin/spring-plugins/spring-plugin-common/src/main/java/cn/hippo4j/agent/plugin/spring/common/support/ThreadPoolMonitorSupport.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.agent.plugin.spring.common.support;
+
+import cn.hippo4j.agent.plugin.spring.common.monitor.MonitorHandlersConfigurator;
+import cn.hippo4j.agent.plugin.spring.common.monitor.MonitorMetricEndpoint;
+import cn.hippo4j.common.executor.ThreadFactoryBuilder;
+import cn.hippo4j.common.executor.ThreadPoolExecutorRegistry;
+import cn.hippo4j.common.extension.spi.ServiceLoaderRegistry;
+import cn.hippo4j.common.monitor.MonitorCollectTypeEnum;
+import cn.hippo4j.common.toolkit.StringUtil;
+import cn.hippo4j.threadpool.dynamic.mode.config.properties.BootstrapConfigProperties;
+import cn.hippo4j.threadpool.dynamic.mode.config.properties.MonitorProperties;
+import cn.hippo4j.threadpool.monitor.api.ThreadPoolMonitor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.core.env.ConfigurableEnvironment;
+import org.springframework.core.env.Environment;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import static cn.hippo4j.agent.plugin.spring.common.support.SpringPropertiesLoader.BOOTSTRAP_CONFIG_PROPERTIES;
+
+/**
+ * This class provides support for monitoring dynamic thread pools in an application.
+ * It includes methods to initialize and enable monitoring components, and schedules
+ * periodic data collection from the thread pools.
+ */
+public class ThreadPoolMonitorSupport {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ThreadPoolMonitorSupport.class);
+
+ private static List threadPoolMonitors;
+
+ static {
+ // Register the ThreadPoolMonitor service with the ServiceLoaderRegistry
+ ServiceLoaderRegistry.register(ThreadPoolMonitor.class);
+ }
+
+ /**
+ * Enables the dynamic thread pool monitoring handler.
+ *
+ * This method performs the following steps:
+ *
+ * - Validates the monitoring configuration from the environment properties.
+ * - Initializes monitoring components for the dynamic thread pools.
+ * - Exposes metric endpoints, such as Prometheus, if configured.
+ * - Schedules periodic collection of metrics from the thread pools.
+ *
+ * If the monitoring configuration is invalid or disabled, the method returns without
+ * enabling the monitoring handler.
+ *
+ *
+ * @param environment The environment from which the monitoring configuration is loaded.
+ */
+ public static void enableThreadPoolMonitorHandler(Environment environment) {
+ BootstrapConfigProperties properties = BOOTSTRAP_CONFIG_PROPERTIES;
+ MonitorProperties monitor = properties.getMonitor();
+ if (Objects.isNull(monitor)
+ || !monitor.getEnable()
+ || StringUtil.isBlank(monitor.getThreadPoolTypes())
+ || StringUtil.isBlank(monitor.getCollectTypes())) {
+ return;
+ }
+
+ LOGGER.info("[Hippo4j-Agent] Start monitoring the running status of dynamic thread pools.");
+ threadPoolMonitors = new ArrayList<>();
+ ScheduledExecutorService collectScheduledExecutor = new ScheduledThreadPoolExecutor(
+ 1,
+ r -> new Thread(r, "client.agent.scheduled.collect.data"));
+
+ // Initialize monitoring components for the dynamic thread pools
+ MonitorHandlersConfigurator.initializeMonitorHandlers(monitor, (ConfigurableEnvironment) environment, threadPoolMonitors);
+
+ // Expose metric endpoints based on the configured collect types
+ List collectTypes = Arrays.asList(monitor.getCollectTypes().split(","));
+ if (collectTypes.contains(MonitorCollectTypeEnum.MICROMETER.getValue())) {
+ MonitorMetricEndpoint.startPrometheusEndpoint();
+ }
+
+ // Schedule periodic collection of metrics from the thread pools
+ collectScheduledExecutor.scheduleWithFixedDelay(
+ scheduleRunnable(),
+ monitor.getInitialDelay(),
+ monitor.getCollectInterval(),
+ TimeUnit.MILLISECONDS);
+
+ if (ThreadPoolExecutorRegistry.getThreadPoolExecutorSize() > 0) {
+ LOGGER.info("[Hippo4j-Agent] Dynamic thread pool: [{}]. The dynamic thread pool starts data collection and reporting.", ThreadPoolExecutorRegistry.getThreadPoolExecutorSize());
+ }
+ }
+
+ /**
+ * Returns a Runnable task that collects metrics from the dynamic thread pools.
+ *
+ * This method is used to create a task that periodically iterates over the
+ * registered thread pool monitors and collects their metrics. If an exception
+ * occurs during the collection, it is logged.
+ *
+ *
+ * @return A Runnable task that performs the metrics collection.
+ */
+ private static Runnable scheduleRunnable() {
+ return () -> {
+ for (ThreadPoolMonitor each : threadPoolMonitors) {
+ try {
+ each.collect();
+ } catch (Exception ex) {
+ LOGGER.error("[Hippo4j-Agent] Error monitoring the running status of dynamic thread pool. Type: {}", each.getType(), ex);
+ }
+ }
+ };
+ }
+
+}
diff --git a/examples/threadpool-example/agent/agent-example-core/src/main/java/cn/hippo4j/example/agent/core/inittest/AlarmSendMessageTest.java b/examples/threadpool-example/agent/agent-example-core/src/main/java/cn/hippo4j/example/agent/core/inittest/AlarmSendMessageTest.java
index ccf385db..bf67eb75 100644
--- a/examples/threadpool-example/agent/agent-example-core/src/main/java/cn/hippo4j/example/agent/core/inittest/AlarmSendMessageTest.java
+++ b/examples/threadpool-example/agent/agent-example-core/src/main/java/cn/hippo4j/example/agent/core/inittest/AlarmSendMessageTest.java
@@ -48,7 +48,7 @@ public class AlarmSendMessageTest {
* If you need to run this single test, add @PostConstruct to the method.
*/
@SuppressWarnings("all")
- @PostConstruct
+ // @PostConstruct
public void alarmSendMessageTest() {
ScheduledExecutorService scheduledThreadPool = Executors.newSingleThreadScheduledExecutor();
scheduledThreadPool.scheduleWithFixedDelay(() -> {
diff --git a/examples/threadpool-example/agent/config-nacos/pom.xml b/examples/threadpool-example/agent/config-nacos/pom.xml
index 4240c147..8134d488 100644
--- a/examples/threadpool-example/agent/config-nacos/pom.xml
+++ b/examples/threadpool-example/agent/config-nacos/pom.xml
@@ -44,14 +44,6 @@
slf4j-api
1.7.21
-
- io.micrometer
- micrometer-registry-prometheus
-
-
- org.springframework.boot
- spring-boot-starter-actuator
-
diff --git a/infra/common/src/main/java/cn/hippo4j/common/monitor/MonitorCollectTypeEnum.java b/infra/common/src/main/java/cn/hippo4j/common/monitor/MonitorCollectTypeEnum.java
new file mode 100644
index 00000000..b7a4a8b1
--- /dev/null
+++ b/infra/common/src/main/java/cn/hippo4j/common/monitor/MonitorCollectTypeEnum.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.common.monitor;
+
+/**
+ * MonitorCollect type enum.
+ */
+public enum MonitorCollectTypeEnum {
+
+ /**
+ * Micrometer
+ */
+ MICROMETER("micrometer"),
+
+ /**
+ * ELASTICSEARCH
+ */
+ ELASTICSEARCH("elasticsearch"),
+
+ /**
+ * LOG
+ */
+ LOG("log");
+
+ private final String value;
+
+ MonitorCollectTypeEnum(String value) {
+ this.value = value;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+}
diff --git a/infra/common/src/main/java/cn/hippo4j/common/monitor/MonitorHandlerTypeEnum.java b/infra/common/src/main/java/cn/hippo4j/common/monitor/MonitorHandlerTypeEnum.java
new file mode 100644
index 00000000..5cf7ad65
--- /dev/null
+++ b/infra/common/src/main/java/cn/hippo4j/common/monitor/MonitorHandlerTypeEnum.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cn.hippo4j.common.monitor;
+
+/**
+ * MonitorHandler type enum.
+ */
+public enum MonitorHandlerTypeEnum {
+ /**
+ * DYNAMIC
+ */
+ DYNAMIC,
+ /**
+ * WEB
+ */
+ WEB,
+ /**
+ * ADAPTER
+ */
+ ADAPTER
+}
diff --git a/kernel/dynamic/mode/config/src/main/java/cn/hippo4j/threadpool/dynamic/mode/config/properties/MonitorProperties.java b/kernel/dynamic/mode/config/src/main/java/cn/hippo4j/threadpool/dynamic/mode/config/properties/MonitorProperties.java
index 38cb1ed1..db4db930 100644
--- a/kernel/dynamic/mode/config/src/main/java/cn/hippo4j/threadpool/dynamic/mode/config/properties/MonitorProperties.java
+++ b/kernel/dynamic/mode/config/src/main/java/cn/hippo4j/threadpool/dynamic/mode/config/properties/MonitorProperties.java
@@ -51,4 +51,9 @@ public class MonitorProperties {
* Collect interval. unit: ms
*/
private Long collectInterval = 5000L;
+
+ /**
+ * Agent micrometer exposed port
+ */
+ private Integer agentMicrometerPort;
}