diff --git a/CHANGES.md b/CHANGES.md
index e3b12390..97e1c6e4 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -7,6 +7,7 @@ Apollo Java 2.4.0
------------------
* [Fix the Cannot enhance @Configuration bean definition issue](https://github.com/apolloconfig/apollo-java/pull/82)
* [Feature openapi query namespace support not fill item](https://github.com/apolloconfig/apollo-java/pull/83)
+* [Add more observability in apollo config client](https://github.com/apolloconfig/apollo-java/pull/74)
------------------
All issues and pull requests are [here](https://github.com/apolloconfig/apollo-java/milestone/4?closed=1)
diff --git a/apollo-client/pom.xml b/apollo-client/pom.xml
index 7a8e436a..f643256a 100644
--- a/apollo-client/pom.xml
+++ b/apollo-client/pom.xml
@@ -97,6 +97,17 @@
mockserver-netty
test
+
+ com.github.noconnor
+ junitperf
+ test
+
+
+ ch.qos.logback
+ logback-classic
+
+
+
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/ConfigService.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/ConfigService.java
index 184c5ee3..02ccdc36 100644
--- a/apollo-client/src/main/java/com/ctrip/framework/apollo/ConfigService.java
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/ConfigService.java
@@ -20,6 +20,8 @@
import com.ctrip.framework.apollo.core.ConfigConsts;
import com.ctrip.framework.apollo.core.enums.ConfigFileFormat;
import com.ctrip.framework.apollo.internals.ConfigManager;
+import com.ctrip.framework.apollo.internals.ConfigMonitorInitializer;
+import com.ctrip.framework.apollo.monitor.api.ConfigMonitor;
import com.ctrip.framework.apollo.spi.ConfigFactory;
import com.ctrip.framework.apollo.spi.ConfigRegistry;
@@ -30,19 +32,31 @@
*/
public class ConfigService {
private static final ConfigService s_instance = new ConfigService();
-
+ private volatile ConfigMonitor m_configMonitor;
private volatile ConfigManager m_configManager;
private volatile ConfigRegistry m_configRegistry;
-
+
+ private ConfigMonitor getMonitor() {
+ getManager();
+ if (m_configMonitor == null) {
+ synchronized (this) {
+ if (m_configMonitor == null) {
+ m_configMonitor = ApolloInjector.getInstance(ConfigMonitor.class);
+ }
+ }
+ }
+ return m_configMonitor;
+ }
+
private ConfigManager getManager() {
if (m_configManager == null) {
synchronized (this) {
if (m_configManager == null) {
m_configManager = ApolloInjector.getInstance(ConfigManager.class);
+ ConfigMonitorInitializer.initialize();
}
}
}
-
return m_configManager;
}
@@ -81,6 +95,10 @@ public static ConfigFile getConfigFile(String namespace, ConfigFileFormat config
return s_instance.getManager().getConfigFile(namespace, configFileFormat);
}
+ public static ConfigMonitor getConfigMonitor(){
+ return s_instance.getMonitor();
+ }
+
static void setConfig(Config config) {
setConfig(ConfigConsts.NAMESPACE_APPLICATION, config);
}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/AbstractConfig.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/AbstractConfig.java
index 7ce41dca..22ba6cde 100644
--- a/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/AbstractConfig.java
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/AbstractConfig.java
@@ -53,7 +53,7 @@
public abstract class AbstractConfig implements Config {
private static final Logger logger = LoggerFactory.getLogger(AbstractConfig.class);
- private static final ExecutorService m_executorService;
+ protected static final ExecutorService m_executorService;
private final List m_listeners = Lists.newCopyOnWriteArrayList();
private final Map> m_interestedKeys = Maps.newConcurrentMap();
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/AbstractConfigFile.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/AbstractConfigFile.java
index 3a1c0df0..5ef1d37f 100644
--- a/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/AbstractConfigFile.java
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/AbstractConfigFile.java
@@ -16,6 +16,8 @@
*/
package com.ctrip.framework.apollo.internals;
+
+import static com.ctrip.framework.apollo.monitor.internal.ApolloClientMonitorConstant.*;
import com.ctrip.framework.apollo.build.ApolloInjector;
import com.ctrip.framework.apollo.core.utils.DeferredLoggerFactory;
import com.ctrip.framework.apollo.enums.ConfigSourceType;
@@ -43,7 +45,7 @@
*/
public abstract class AbstractConfigFile implements ConfigFile, RepositoryChangeListener {
private static final Logger logger = DeferredLoggerFactory.getLogger(AbstractConfigFile.class);
- private static ExecutorService m_executorService;
+ protected static ExecutorService m_executorService;
protected final ConfigRepository m_configRepository;
protected final String m_namespace;
protected final AtomicReference m_configProperties;
@@ -112,7 +114,7 @@ public synchronized void onRepositoryChange(String namespace, Properties newProp
this.fireConfigChange(new ConfigFileChangeEvent(m_namespace, oldValue, newValue, changeType));
- Tracer.logEvent("Apollo.Client.ConfigChanges", m_namespace);
+ Tracer.logEvent(APOLLO_CLIENT_CONFIGCHANGES, m_namespace);
}
@Override
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/AbstractConfigRepository.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/AbstractConfigRepository.java
index b4505297..6b00c4c0 100644
--- a/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/AbstractConfigRepository.java
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/AbstractConfigRepository.java
@@ -16,6 +16,7 @@
*/
package com.ctrip.framework.apollo.internals;
+import static com.ctrip.framework.apollo.monitor.internal.ApolloClientMonitorConstant.*;
import com.ctrip.framework.apollo.build.ApolloInjector;
import com.ctrip.framework.apollo.util.factory.PropertiesFactory;
import java.util.List;
@@ -41,7 +42,7 @@ protected boolean trySync() {
sync();
return true;
} catch (Throwable ex) {
- Tracer.logEvent("ApolloConfigException", ExceptionUtil.getDetailMessage(ex));
+ Tracer.logEvent(APOLLO_CONFIG_EXCEPTION, ExceptionUtil.getDetailMessage(ex));
logger
.warn("Sync config failed, will retry. Repository {}, reason: {}", this.getClass(), ExceptionUtil
.getDetailMessage(ex));
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/ConfigMonitorInitializer.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/ConfigMonitorInitializer.java
new file mode 100644
index 00000000..a6c0f421
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/ConfigMonitorInitializer.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.internals;
+
+import static com.ctrip.framework.apollo.monitor.internal.ApolloClientMonitorConstant.MBEAN_NAME;
+
+import com.ctrip.framework.apollo.build.ApolloInjector;
+import com.ctrip.framework.apollo.core.utils.ClassLoaderUtil;
+import com.ctrip.framework.apollo.monitor.internal.exporter.AbstractApolloClientMetricsExporter;
+import com.ctrip.framework.apollo.monitor.internal.exporter.ApolloClientMetricsExporter;
+import com.ctrip.framework.apollo.monitor.internal.jmx.ApolloClientJmxMBeanRegister;
+import com.ctrip.framework.apollo.monitor.internal.ApolloClientMonitorContext;
+import com.ctrip.framework.apollo.monitor.internal.listener.impl.DefaultApolloClientBootstrapArgsApi;
+import com.ctrip.framework.apollo.monitor.internal.listener.impl.DefaultApolloClientExceptionApi;
+import com.ctrip.framework.apollo.monitor.internal.listener.impl.DefaultApolloClientNamespaceApi;
+import com.ctrip.framework.apollo.monitor.internal.listener.impl.DefaultApolloClientThreadPoolApi;
+import com.ctrip.framework.apollo.monitor.internal.exporter.ApolloClientMetricsExporterFactory;
+import com.ctrip.framework.apollo.monitor.internal.tracer.ApolloClientMonitorMessageProducer;
+import com.ctrip.framework.apollo.monitor.internal.tracer.ApolloClientMessageProducerComposite;
+import com.ctrip.framework.apollo.tracer.internals.NullMessageProducer;
+import com.ctrip.framework.apollo.tracer.internals.cat.CatMessageProducer;
+import com.ctrip.framework.apollo.tracer.internals.cat.CatNames;
+import com.ctrip.framework.apollo.tracer.spi.MessageProducer;
+import com.ctrip.framework.apollo.util.ConfigUtil;
+import com.ctrip.framework.foundation.internals.ServiceBootstrap;
+import com.google.common.collect.Lists;
+import java.util.List;
+
+/**
+ * ConfigMonitorInitializer initializes the Apollo Config Monitor.
+ */
+public class ConfigMonitorInitializer {
+
+ private static final ApolloClientMonitorContext MONITOR_CONTEXT = ApolloInjector.getInstance(
+ ApolloClientMonitorContext.class);
+ protected static volatile boolean hasInitialized = false;
+ private static ConfigUtil m_configUtil = ApolloInjector.getInstance(ConfigUtil.class);
+
+ public static void initialize() {
+ if (m_configUtil.isClientMonitorEnabled() && !hasInitialized) {
+ synchronized (ConfigMonitorInitializer.class) {
+ if (!hasInitialized) {
+ doInit();
+ hasInitialized = true;
+ }
+ }
+ }
+ }
+
+ private static void doInit() {
+ initializeMetricsEventListener();
+ initializeMetricsExporter();
+ initializeJmxMonitoring();
+ }
+
+
+ private static void initializeJmxMonitoring() {
+ if (m_configUtil.isClientMonitorJmxEnabled()) {
+ MONITOR_CONTEXT.getApolloClientMonitorEventListeners().forEach(metricsListener ->
+ ApolloClientJmxMBeanRegister.register(
+ MBEAN_NAME + metricsListener.getName(), metricsListener)
+ );
+ }
+ }
+
+ private static void initializeMetricsEventListener() {
+ ConfigManager configManager = ApolloInjector.getInstance(
+ ConfigManager.class);
+ DefaultApolloClientBootstrapArgsApi defaultApolloClientBootstrapArgsApi = new DefaultApolloClientBootstrapArgsApi(
+ m_configUtil);
+ DefaultApolloClientExceptionApi defaultApolloClientExceptionApi = new DefaultApolloClientExceptionApi(m_configUtil);
+ DefaultApolloClientNamespaceApi defaultApolloClientNamespaceApi = new DefaultApolloClientNamespaceApi(
+ configManager);
+ DefaultApolloClientThreadPoolApi defaultApolloClientThreadPoolApi = new DefaultApolloClientThreadPoolApi(
+ RemoteConfigRepository.m_executorService,
+ AbstractConfig.m_executorService, AbstractConfigFile.m_executorService,
+ AbstractApolloClientMetricsExporter.m_executorService);
+
+ MONITOR_CONTEXT.setApolloClientBootstrapArgsMonitorApi(defaultApolloClientBootstrapArgsApi);
+ MONITOR_CONTEXT.setApolloClientExceptionMonitorApi(defaultApolloClientExceptionApi);
+ MONITOR_CONTEXT.setApolloClientNamespaceMonitorApi(defaultApolloClientNamespaceApi);
+ MONITOR_CONTEXT.setApolloClientThreadPoolMonitorApi(defaultApolloClientThreadPoolApi);
+ MONITOR_CONTEXT.setApolloClientMonitorEventListeners(
+ Lists.newArrayList(defaultApolloClientBootstrapArgsApi,
+ defaultApolloClientNamespaceApi, defaultApolloClientThreadPoolApi,
+ defaultApolloClientExceptionApi));
+ }
+
+ private static void initializeMetricsExporter(
+ ) {
+ ApolloClientMetricsExporterFactory exporterFactory = ApolloInjector.getInstance(
+ ApolloClientMetricsExporterFactory.class);
+ ApolloClientMetricsExporter metricsReporter = exporterFactory.getMetricsReporter(
+ MONITOR_CONTEXT.getApolloClientMonitorEventListeners());
+ if (metricsReporter != null) {
+ MONITOR_CONTEXT.setApolloClientMetricsExporter(metricsReporter);
+ }
+ }
+
+ public static ApolloClientMessageProducerComposite initializeMessageProducerComposite() {
+ List producers = ServiceBootstrap.loadAllOrdered(MessageProducer.class);
+
+ if (m_configUtil.isClientMonitorEnabled()) {
+ producers.add(new ApolloClientMonitorMessageProducer());
+ }
+
+ if (ClassLoaderUtil.isClassPresent(CatNames.CAT_CLASS)) {
+ producers.add(new CatMessageProducer());
+ }
+
+ if (producers.isEmpty()) {
+ producers.add(new NullMessageProducer());
+ }
+
+ return new ApolloClientMessageProducerComposite(producers);
+ }
+
+ // for test only
+ protected static void reset() {
+ hasInitialized = false;
+ m_configUtil = ApolloInjector.getInstance(ConfigUtil.class);
+
+ }
+}
\ No newline at end of file
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/ConfigServiceLocator.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/ConfigServiceLocator.java
index ce9b097c..0ea8e07c 100644
--- a/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/ConfigServiceLocator.java
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/ConfigServiceLocator.java
@@ -16,6 +16,8 @@
*/
package com.ctrip.framework.apollo.internals;
+import static com.ctrip.framework.apollo.monitor.internal.ApolloClientMonitorConstant.*;
+
import com.ctrip.framework.apollo.core.ApolloClientSystemConsts;
import com.ctrip.framework.apollo.core.ServiceNameConsts;
import com.ctrip.framework.apollo.core.utils.DeferredLoggerFactory;
@@ -218,7 +220,7 @@ private void schedulePeriodicRefresh() {
@Override
public void run() {
logger.debug("refresh config services");
- Tracer.logEvent("Apollo.MetaService", "periodicRefresh");
+ Tracer.logEvent(APOLLO_META_SERVICE, "periodicRefresh");
tryUpdateConfigServices();
}
}, m_configUtil.getRefreshInterval(), m_configUtil.getRefreshInterval(),
@@ -258,7 +260,7 @@ private synchronized void updateConfigServices() {
setConfigServices(services);
return;
} catch (Throwable ex) {
- Tracer.logEvent("ApolloConfigException", ExceptionUtil.getDetailMessage(ex));
+ Tracer.logEvent(APOLLO_CONFIG_EXCEPTION, ExceptionUtil.getDetailMessage(ex));
transaction.setStatus(ex);
exception = ex;
} finally {
@@ -302,6 +304,6 @@ private void logConfigServices(List serviceDtos) {
}
private void logConfigService(String serviceUrl) {
- Tracer.logEvent("Apollo.Config.Services", serviceUrl);
+ Tracer.logEvent(APOLLO_CONFIG_SERVICES, serviceUrl);
}
}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/DefaultConfig.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/DefaultConfig.java
index 750abd0e..586391f0 100644
--- a/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/DefaultConfig.java
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/DefaultConfig.java
@@ -16,6 +16,7 @@
*/
package com.ctrip.framework.apollo.internals;
+import static com.ctrip.framework.apollo.monitor.internal.ApolloClientMonitorConstant.*;
import com.ctrip.framework.apollo.core.utils.DeferredLoggerFactory;
import com.ctrip.framework.apollo.enums.ConfigSourceType;
import com.google.common.collect.Maps;
@@ -236,7 +237,7 @@ public synchronized void onRepositoryChange(String namespace, Properties newProp
this.fireConfigChange(m_namespace, actualChanges);
- Tracer.logEvent("Apollo.Client.ConfigChanges", m_namespace);
+ Tracer.logEvent(APOLLO_CLIENT_CONFIGCHANGES, m_namespace);
}
private void updateConfig(Properties newConfigProperties, ConfigSourceType sourceType) {
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/DefaultConfigManager.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/DefaultConfigManager.java
index e897b6f7..81c4081c 100644
--- a/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/DefaultConfigManager.java
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/DefaultConfigManager.java
@@ -16,15 +16,18 @@
*/
package com.ctrip.framework.apollo.internals;
-import java.util.Map;
+import static com.ctrip.framework.apollo.monitor.internal.ApolloClientMonitorConstant.APOLLO_CLIENT_NAMESPACE_USAGE;
import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.ConfigFile;
import com.ctrip.framework.apollo.build.ApolloInjector;
import com.ctrip.framework.apollo.core.enums.ConfigFileFormat;
+import com.ctrip.framework.apollo.enums.ConfigSourceType;
import com.ctrip.framework.apollo.spi.ConfigFactory;
import com.ctrip.framework.apollo.spi.ConfigFactoryManager;
+import com.ctrip.framework.apollo.tracer.Tracer;
import com.google.common.collect.Maps;
+import java.util.Map;
/**
* @author Jason Song(song_s@ctrip.com)
@@ -44,7 +47,7 @@ public DefaultConfigManager() {
@Override
public Config getConfig(String namespace) {
Config config = m_configs.get(namespace);
-
+
if (config == null) {
Object lock = m_configLocks.computeIfAbsent(namespace, key -> new Object());
synchronized (lock) {
@@ -58,6 +61,9 @@ public Config getConfig(String namespace) {
}
}
}
+ if (!ConfigSourceType.NONE.equals(config.getSourceType())) {
+ Tracer.logMetricsForCount(APOLLO_CLIENT_NAMESPACE_USAGE + ":" + namespace);
+ }
return config;
}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/DefaultInjector.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/DefaultInjector.java
index 707ad6c5..5d414070 100644
--- a/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/DefaultInjector.java
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/DefaultInjector.java
@@ -17,6 +17,11 @@
package com.ctrip.framework.apollo.internals;
import com.ctrip.framework.apollo.exceptions.ApolloConfigException;
+import com.ctrip.framework.apollo.monitor.api.ConfigMonitor;
+import com.ctrip.framework.apollo.monitor.internal.DefaultConfigMonitor;
+import com.ctrip.framework.apollo.monitor.internal.exporter.impl.DefaultApolloClientMetricsExporterFactory;
+import com.ctrip.framework.apollo.monitor.internal.ApolloClientMonitorContext;
+import com.ctrip.framework.apollo.monitor.internal.exporter.ApolloClientMetricsExporterFactory;
import com.ctrip.framework.apollo.spi.ApolloInjectorCustomizer;
import com.ctrip.framework.apollo.spi.ConfigFactory;
import com.ctrip.framework.apollo.spi.ConfigFactoryManager;
@@ -30,7 +35,6 @@
import com.ctrip.framework.apollo.util.factory.PropertiesFactory;
import com.ctrip.framework.apollo.util.http.DefaultHttpClient;
import com.ctrip.framework.apollo.util.http.HttpClient;
-
import com.ctrip.framework.apollo.util.yaml.YamlParser;
import com.ctrip.framework.foundation.internals.ServiceBootstrap;
import com.google.inject.AbstractModule;
@@ -105,6 +109,9 @@ protected void configure() {
bind(RemoteConfigLongPollService.class).in(Singleton.class);
bind(YamlParser.class).in(Singleton.class);
bind(PropertiesFactory.class).to(DefaultPropertiesFactory.class).in(Singleton.class);
+ bind(ConfigMonitor.class).to(DefaultConfigMonitor.class).in(Singleton.class);
+ bind(ApolloClientMonitorContext.class).in(Singleton.class);
+ bind(ApolloClientMetricsExporterFactory.class).to(DefaultApolloClientMetricsExporterFactory.class).in(Singleton.class);
}
}
}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/LocalFileConfigRepository.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/LocalFileConfigRepository.java
index 1820fd1a..52786384 100644
--- a/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/LocalFileConfigRepository.java
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/LocalFileConfigRepository.java
@@ -16,6 +16,7 @@
*/
package com.ctrip.framework.apollo.internals;
+import static com.ctrip.framework.apollo.monitor.internal.ApolloClientMonitorConstant.*;
import com.ctrip.framework.apollo.core.utils.DeferredLoggerFactory;
import com.ctrip.framework.apollo.enums.ConfigSourceType;
import java.io.File;
@@ -154,7 +155,7 @@ protected void sync() {
m_sourceType = ConfigSourceType.LOCAL;
transaction.setStatus(Transaction.SUCCESS);
} catch (Throwable ex) {
- Tracer.logEvent("ApolloConfigException", ExceptionUtil.getDetailMessage(ex));
+ Tracer.logEvent(APOLLO_CONFIG_EXCEPTION, ExceptionUtil.getDetailMessage(ex));
transaction.setStatus(ex);
exception = ex;
//ignore
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/RemoteConfigLongPollService.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/RemoteConfigLongPollService.java
index 59806b2a..ccc75576 100644
--- a/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/RemoteConfigLongPollService.java
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/RemoteConfigLongPollService.java
@@ -16,6 +16,7 @@
*/
package com.ctrip.framework.apollo.internals;
+import static com.ctrip.framework.apollo.monitor.internal.ApolloClientMonitorConstant.*;
import com.ctrip.framework.apollo.build.ApolloInjector;
import com.ctrip.framework.apollo.core.ConfigConsts;
import com.ctrip.framework.apollo.core.dto.ApolloConfigNotification;
@@ -33,9 +34,9 @@
import com.ctrip.framework.apollo.tracer.spi.Transaction;
import com.ctrip.framework.apollo.util.ConfigUtil;
import com.ctrip.framework.apollo.util.ExceptionUtil;
+import com.ctrip.framework.apollo.util.http.HttpClient;
import com.ctrip.framework.apollo.util.http.HttpRequest;
import com.ctrip.framework.apollo.util.http.HttpResponse;
-import com.ctrip.framework.apollo.util.http.HttpClient;
import com.ctrip.framework.foundation.internals.ServiceBootstrap;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
@@ -50,6 +51,7 @@
import com.google.common.util.concurrent.RateLimiter;
import com.google.gson.Gson;
import java.lang.reflect.Type;
+import java.net.SocketTimeoutException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;
@@ -209,9 +211,12 @@ private void doLongPollingRefresh(String appId, String cluster, String dataCente
transaction.setStatus(Transaction.SUCCESS);
} catch (Throwable ex) {
lastServiceDto = null;
- Tracer.logEvent("ApolloConfigException", ExceptionUtil.getDetailMessage(ex));
+ Tracer.logEvent(APOLLO_CONFIG_EXCEPTION, ExceptionUtil.getDetailMessage(ex));
transaction.setStatus(ex);
long sleepTimeInSecond = m_longPollFailSchedulePolicyInSecond.fail();
+ if (ex.getCause() instanceof SocketTimeoutException) {
+ Tracer.logEvent(APOLLO_CLIENT_NAMESPACE_TIMEOUT, assembleNamespaces());
+ }
logger.warn(
"Long polling failed, will retry in {} seconds. appId: {}, cluster: {}, namespaces: {}, long polling url: {}, reason: {}",
sleepTimeInSecond, appId, cluster, assembleNamespaces(), url, ExceptionUtil.getDetailMessage(ex));
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/RemoteConfigRepository.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/RemoteConfigRepository.java
index c3aab684..879f243c 100644
--- a/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/RemoteConfigRepository.java
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/RemoteConfigRepository.java
@@ -16,6 +16,8 @@
*/
package com.ctrip.framework.apollo.internals;
+import static com.ctrip.framework.apollo.monitor.internal.ApolloClientMonitorConstant.*;
+
import com.ctrip.framework.apollo.Apollo;
import com.ctrip.framework.apollo.build.ApolloInjector;
import com.ctrip.framework.apollo.core.ConfigConsts;
@@ -35,9 +37,9 @@
import com.ctrip.framework.apollo.tracer.spi.Transaction;
import com.ctrip.framework.apollo.util.ConfigUtil;
import com.ctrip.framework.apollo.util.ExceptionUtil;
+import com.ctrip.framework.apollo.util.http.HttpClient;
import com.ctrip.framework.apollo.util.http.HttpRequest;
import com.ctrip.framework.apollo.util.http.HttpResponse;
-import com.ctrip.framework.apollo.util.http.HttpClient;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
@@ -73,7 +75,7 @@ public class RemoteConfigRepository extends AbstractConfigRepository {
private final RemoteConfigLongPollService remoteConfigLongPollService;
private volatile AtomicReference m_configCache;
private final String m_namespace;
- private final static ScheduledExecutorService m_executorService;
+ protected final static ScheduledExecutorService m_executorService;
private final AtomicReference m_longPollServiceDto;
private final AtomicReference m_remoteMessages;
private final RateLimiter m_loadConfigRateLimiter;
@@ -111,7 +113,10 @@ public RemoteConfigRepository(String namespace) {
@Override
public Properties getConfig() {
if (m_configCache.get() == null) {
+ long start = System.currentTimeMillis();
this.sync();
+ Tracer.logEvent(APOLLO_CLIENT_NAMESPACE_FIRST_LOAD_SPEND+":"+m_namespace,
+ String.valueOf(System.currentTimeMillis() - start));
}
return transformApolloConfigToProperties(m_configCache.get());
}
@@ -133,10 +138,10 @@ private void schedulePeriodicRefresh() {
new Runnable() {
@Override
public void run() {
- Tracer.logEvent("Apollo.ConfigService", String.format("periodicRefresh: %s", m_namespace));
+ Tracer.logEvent(APOLLO_CONFIGSERVICE, String.format("periodicRefresh: %s", m_namespace));
logger.debug("refresh config for namespace: {}", m_namespace);
trySync();
- Tracer.logEvent("Apollo.Client.Version", Apollo.VERSION);
+ Tracer.logEvent(APOLLO_CLIENT_VERSION, Apollo.VERSION);
}
}, m_configUtil.getRefreshInterval(), m_configUtil.getRefreshInterval(),
m_configUtil.getRefreshIntervalTimeUnit());
@@ -158,7 +163,7 @@ protected synchronized void sync() {
}
if (current != null) {
- Tracer.logEvent(String.format("Apollo.Client.Configs.%s", current.getNamespaceName()),
+ Tracer.logEvent(String.format(APOLLO_CLIENT_CONFIGS+"%s", current.getNamespaceName()),
current.getReleaseKey());
}
@@ -189,7 +194,7 @@ private ApolloConfig loadApolloConfig() {
String cluster = m_configUtil.getCluster();
String dataCenter = m_configUtil.getDataCenter();
String secret = m_configUtil.getAccessKeySecret();
- Tracer.logEvent("Apollo.Client.ConfigMeta", STRING_JOINER.join(appId, cluster, m_namespace));
+ Tracer.logEvent(APOLLO_CLIENT_CONFIGMETA, STRING_JOINER.join(appId, cluster, m_namespace));
int maxRetries = m_configNeedForceRefresh.get() ? 2 : 1;
long onErrorSleepTime = 0; // 0 means no sleep
Throwable exception = null;
@@ -260,15 +265,17 @@ private ApolloConfig loadApolloConfig() {
appId, cluster, m_namespace);
statusCodeException = new ApolloConfigStatusCodeException(ex.getStatusCode(),
message);
+ Tracer.logEvent(APOLLO_CLIENT_NAMESPACE_NOT_FOUND,m_namespace);
+
}
- Tracer.logEvent("ApolloConfigException", ExceptionUtil.getDetailMessage(statusCodeException));
+ Tracer.logEvent(APOLLO_CONFIG_EXCEPTION, ExceptionUtil.getDetailMessage(statusCodeException));
transaction.setStatus(statusCodeException);
exception = statusCodeException;
if(ex.getStatusCode() == 404) {
break retryLoopLabel;
}
} catch (Throwable ex) {
- Tracer.logEvent("ApolloConfigException", ExceptionUtil.getDetailMessage(ex));
+ Tracer.logEvent(APOLLO_CONFIG_EXCEPTION, ExceptionUtil.getDetailMessage(ex));
transaction.setStatus(ex);
exception = ex;
} finally {
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/SimpleConfig.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/SimpleConfig.java
index c8897bbd..2d333988 100644
--- a/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/SimpleConfig.java
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/SimpleConfig.java
@@ -16,6 +16,7 @@
*/
package com.ctrip.framework.apollo.internals;
+import static com.ctrip.framework.apollo.monitor.internal.ApolloClientMonitorConstant.*;
import com.ctrip.framework.apollo.enums.ConfigSourceType;
import java.util.Collections;
import java.util.List;
@@ -113,7 +114,7 @@ public String apply(ConfigChange input) {
this.fireConfigChange(m_namespace, changeMap);
- Tracer.logEvent("Apollo.Client.ConfigChanges", m_namespace);
+ Tracer.logEvent(APOLLO_CLIENT_CONFIGCHANGES, m_namespace);
}
private void updateConfig(Properties newConfigProperties, ConfigSourceType sourceType) {
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/YamlConfigFile.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/YamlConfigFile.java
index 3443d582..05f87569 100644
--- a/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/YamlConfigFile.java
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/internals/YamlConfigFile.java
@@ -16,6 +16,7 @@
*/
package com.ctrip.framework.apollo.internals;
+import static com.ctrip.framework.apollo.monitor.internal.ApolloClientMonitorConstant.*;
import com.ctrip.framework.apollo.util.ExceptionUtil;
import java.util.Properties;
@@ -65,7 +66,7 @@ private boolean tryTransformToProperties() {
transformToProperties();
return true;
} catch (Throwable ex) {
- Tracer.logEvent("ApolloConfigException", ExceptionUtil.getDetailMessage(ex));
+ Tracer.logEvent(APOLLO_CONFIG_EXCEPTION, ExceptionUtil.getDetailMessage(ex));
logger.warn("yaml to properties failed, reason: {}", ExceptionUtil.getDetailMessage(ex));
}
return false;
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/api/ApolloClientBootstrapArgsMonitorApi.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/api/ApolloClientBootstrapArgsMonitorApi.java
new file mode 100644
index 00000000..cd5c33cc
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/api/ApolloClientBootstrapArgsMonitorApi.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2022 Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.api;
+
+import static com.ctrip.framework.apollo.core.ApolloClientSystemConsts.*;
+import static com.ctrip.framework.apollo.core.ConfigConsts.APOLLO_AUTO_UPDATE_INJECTED_SPRING_PROPERTIES;
+import static com.ctrip.framework.apollo.monitor.internal.ApolloClientMonitorConstant.*;
+import static com.ctrip.framework.apollo.spring.config.PropertySourcesConstants.*;
+
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * @author Rawven
+ */
+public interface ApolloClientBootstrapArgsMonitorApi {
+
+ /**
+ * get startup params by key
+ */
+ default Object getStartupArg(String key) {
+ return getBootstrapArgs().get(key);
+ }
+
+ /**
+ * get bootstrap args map
+ */
+ default Map getBootstrapArgs() {
+ return Collections.emptyMap();
+ }
+
+ default String getConfigServiceUrl() {
+ return (String) getBootstrapArgs().getOrDefault(CONFIG_SERVICE_URL, "");
+ }
+
+ default String getAccessKeySecret() {
+ return (String) getBootstrapArgs().getOrDefault(APOLLO_ACCESS_KEY_SECRET, "");
+ }
+
+ default Boolean getAutoUpdateInjectedSpringProperties() {
+ return (Boolean) getBootstrapArgs().getOrDefault(APOLLO_AUTO_UPDATE_INJECTED_SPRING_PROPERTIES,
+ false);
+ }
+
+ default Boolean isBootstrapEnabled() {
+ return (Boolean) getBootstrapArgs().getOrDefault(APOLLO_BOOTSTRAP_ENABLED, false);
+ }
+
+ default String getBootstrapNamespaces() {
+ return (String) getBootstrapArgs().getOrDefault(APOLLO_BOOTSTRAP_NAMESPACES, "");
+ }
+
+ default Boolean isBootstrapEagerLoadEnabled() {
+ return (Boolean) getBootstrapArgs().getOrDefault(APOLLO_BOOTSTRAP_EAGER_LOAD_ENABLED, false);
+ }
+
+ default Boolean isOverrideSystemProperties() {
+ return (Boolean) getBootstrapArgs().getOrDefault(APOLLO_OVERRIDE_SYSTEM_PROPERTIES, false);
+ }
+
+ default String getCacheDir() {
+ return (String) getBootstrapArgs().getOrDefault(APOLLO_CACHE_DIR, "");
+ }
+
+ default String getCluster() {
+ return (String) getBootstrapArgs().getOrDefault(APOLLO_CLUSTER, "");
+ }
+
+ default String getConfigService() {
+ return (String) getBootstrapArgs().getOrDefault(APOLLO_CONFIG_SERVICE, "");
+ }
+
+ default String getClientMonitorExternalForm() {
+ return (String) getBootstrapArgs().getOrDefault(APOLLO_CLIENT_MONITOR_EXTERNAL_TYPE, "");
+ }
+
+ default Boolean isClientMonitorEnabled() {
+ return (Boolean) getBootstrapArgs().getOrDefault(APOLLO_CLIENT_MONITOR_ENABLED, false);
+ }
+
+ default Boolean isClientMonitorJmxEnabled() {
+ return (Boolean) getBootstrapArgs().getOrDefault(APOLLO_CLIENT_MONITOR_JMX_ENABLED, false);
+ }
+
+ default long getClientMonitorExternalExportPeriod() {
+ return (Long) getBootstrapArgs().getOrDefault(APOLLO_CLIENT_MONITOR_EXTERNAL_EXPORT_PERIOD, 0L);
+ }
+
+ default int getClientMonitorExceptionSaveSize() {
+ return (Integer) getBootstrapArgs().getOrDefault(APOLLO_CLIENT_MONITOR_EXCEPTION_QUEUE_SIZE, 0);
+ }
+
+ default String getApolloMeta() {
+ return (String) getBootstrapArgs().getOrDefault(APOLLO_META, "");
+ }
+
+ default String getMetaLatestFreshTime() {
+ return (String) getBootstrapArgs().getOrDefault(META_FRESH, "");
+ }
+
+ default Boolean isPropertyNamesCacheEnable() {
+ return (Boolean) getBootstrapArgs().getOrDefault(APOLLO_PROPERTY_NAMES_CACHE_ENABLE, false);
+ }
+
+ default Boolean isPropertyOrderEnable() {
+ return (Boolean) getBootstrapArgs().getOrDefault(APOLLO_PROPERTY_ORDER_ENABLE, false);
+ }
+
+ default String getVersion() {
+ return (String) getBootstrapArgs().getOrDefault(VERSION, "");
+ }
+
+ default String getEnv() {
+ return (String) getBootstrapArgs().getOrDefault(ENV, "");
+ }
+
+ default String getAppId() {
+ return (String) getBootstrapArgs().getOrDefault(APP_ID, "");
+ }
+}
\ No newline at end of file
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/api/ApolloClientExceptionMonitorApi.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/api/ApolloClientExceptionMonitorApi.java
new file mode 100644
index 00000000..29d2b207
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/api/ApolloClientExceptionMonitorApi.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.api;
+
+import java.util.List;
+
+/**
+ * @author Rawven
+ */
+public interface ApolloClientExceptionMonitorApi {
+
+ /**
+ * Get exception information the number is limited and can be configured through
+ * apollo.client.monitor.exception-queue-size
+ */
+ List getApolloConfigExceptionList();
+
+ Integer getExceptionCountFromStartup();
+
+}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/api/ApolloClientNamespaceMonitorApi.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/api/ApolloClientNamespaceMonitorApi.java
new file mode 100644
index 00000000..fdce6568
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/api/ApolloClientNamespaceMonitorApi.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.api;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * @author Rawven
+ */
+public interface ApolloClientNamespaceMonitorApi {
+
+ /**
+ * NamespaceMetrics: 1.usageCount 2.firstLoadSpend 3.latestUpdateTime 4.releaseKey
+ */
+ Map getNamespaceMetrics();
+
+ /**
+ * get Namespace Config.ItemsNum
+ */
+ Integer getNamespacePropertySize(String namespace);
+
+ /**
+ * get not found namespaces
+ */
+ List getNotFoundNamespaces();
+
+ /**
+ * get timeout namespaces
+ */
+ List getTimeoutNamespaces();
+
+
+ class NamespaceMetrics {
+
+ private AtomicInteger usageCount = new AtomicInteger(0);
+ private long firstLoadTimeSpendInMs;
+ private LocalDateTime latestUpdateTime = LocalDateTime.now();
+ private String releaseKey = "";
+
+ public String getReleaseKey() {
+ return releaseKey;
+ }
+
+ public void setReleaseKey(String releaseKey) {
+ this.releaseKey = releaseKey;
+ }
+
+ public int getUsageCount() {
+ return usageCount.get();
+ }
+
+ public void incrementUsageCount() {
+ usageCount.incrementAndGet();
+ }
+
+ public long getFirstLoadTimeSpendInMs() {
+ return firstLoadTimeSpendInMs;
+ }
+
+ public void setFirstLoadTimeSpendInMs(long firstLoadTimeSpendInMs) {
+ this.firstLoadTimeSpendInMs = firstLoadTimeSpendInMs;
+ }
+
+ public LocalDateTime getLatestUpdateTime() {
+ return latestUpdateTime;
+ }
+
+ public void setLatestUpdateTime(LocalDateTime latestUpdateTime) {
+ this.latestUpdateTime = latestUpdateTime;
+ }
+ }
+
+
+}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/api/ApolloClientThreadPoolMonitorApi.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/api/ApolloClientThreadPoolMonitorApi.java
new file mode 100644
index 00000000..d38555e4
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/api/ApolloClientThreadPoolMonitorApi.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.api;
+
+import java.util.Map;
+import java.util.concurrent.ThreadPoolExecutor;
+
+/**
+ * @author Rawven
+ */
+public interface ApolloClientThreadPoolMonitorApi {
+
+ /**
+ * get thread pool info key "RemoteConfigRepository" ,"AbstractConfig","AbstractConfigFile";
+ */
+ Map getThreadPoolInfo();
+
+ /**
+ * RemoteConfigRepository.m_executorService
+ */
+ ApolloThreadPoolInfo getRemoteConfigRepositoryThreadPoolInfo();
+
+ /**
+ * AbstractConfig.m_executorService
+ */
+ ApolloThreadPoolInfo getAbstractConfigThreadPoolInfo();
+
+ /**
+ * AbstractConfigFile.m_executorService
+ */
+ ApolloThreadPoolInfo getAbstractConfigFileThreadPoolInfo();
+
+ /**
+ * AbstractApolloClientMetricsExporter.m_executorService
+ */
+ ApolloThreadPoolInfo getMetricsExporterThreadPoolInfo();
+
+
+ class ApolloThreadPoolInfo {
+
+ private ThreadPoolExecutor executor;
+
+ public ApolloThreadPoolInfo(ThreadPoolExecutor executor) {
+ this.executor = executor;
+ }
+
+ public ApolloThreadPoolInfo() {
+ }
+
+
+ public int getActiveTaskCount() {
+ return executor != null ? executor.getActiveCount() : 0;
+ }
+
+ public int getQueueSize() {
+ return executor != null ? executor.getQueue().size() : 0;
+ }
+
+ public int getCorePoolSize() {
+ return executor != null ? executor.getCorePoolSize() : 0;
+ }
+
+ public int getMaximumPoolSize() {
+ return executor != null ? executor.getMaximumPoolSize() : 0;
+ }
+
+ public int getPoolSize() {
+ return executor != null ? executor.getPoolSize() : 0;
+ }
+
+ public long getTotalTaskCount() {
+ return executor != null ? executor.getTaskCount() : 0;
+ }
+
+ public long getCompletedTaskCount() {
+ return executor != null ? executor.getCompletedTaskCount() : 0;
+ }
+
+ public int getLargestPoolSize() {
+ return executor != null ? executor.getLargestPoolSize() : 0;
+ }
+
+ public int getQueueRemainingCapacity() {
+ return executor != null ? executor.getQueue().remainingCapacity() : 0;
+ }
+
+ }
+}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/api/ConfigMonitor.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/api/ConfigMonitor.java
new file mode 100644
index 00000000..a1731644
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/api/ConfigMonitor.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.api;
+
+/**
+ * @author Rawven
+ */
+public interface ConfigMonitor {
+
+ /**
+ * get thread pool monitor api
+ */
+ ApolloClientThreadPoolMonitorApi getThreadPoolMonitorApi();
+
+ /**
+ * get exception monitor api
+ */
+ ApolloClientExceptionMonitorApi getExceptionMonitorApi();
+
+ /**
+ * get namespace monitor api
+ */
+ ApolloClientNamespaceMonitorApi getNamespaceMonitorApi();
+
+ /**
+ * get running params monitor api
+ */
+ ApolloClientBootstrapArgsMonitorApi getRunningParamsMonitorApi();
+
+ /**
+ * get monitor external system data
+ */
+ String getExporterData();
+}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/ApolloClientMonitorConstant.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/ApolloClientMonitorConstant.java
new file mode 100644
index 00000000..0a77bdd6
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/ApolloClientMonitorConstant.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal;
+
+/**
+ * @author Rawven
+ */
+public class ApolloClientMonitorConstant {
+
+ /**
+ * common
+ */
+ public static final String MBEAN_NAME = "apollo.client.monitor:type=";
+ public static final String NAMESPACE = "namespace";
+ public static final String TIMESTAMP = "timestamp";
+ public static final String THROWABLE = "throwable";
+ public static final String NAMESPACE_RELEASE_KEY = "releaseKey";
+ public static final String ENV = "env";
+ public static final String VERSION = "version";
+ public static final String META_FRESH = "metaFreshTime";
+ public static final String CONFIG_SERVICE_URL = "configServiceUrl";
+
+ /**
+ * tracer
+ */
+ public static final String APOLLO_CLIENT_CONFIGCHANGES = "Apollo.Client.ConfigChanges";
+ public static final String APOLLO_CONFIG_EXCEPTION = "ApolloConfigException";
+ public static final String APOLLO_META_SERVICE = "Apollo.MetaService";
+ public static final String APOLLO_CONFIG_SERVICES = "Apollo.Config.Services";
+ public static final String APOLLO_CLIENT_VERSION = "Apollo.Client.Version";
+ public static final String APOLLO_CONFIGSERVICE = "Apollo.ConfigService";
+ public static final String APOLLO_CONFIGSERVICE_HELP_STR = "periodicRefresh: ";
+ public static final String APOLLO_CLIENT_CONFIGS = "Apollo.Client.Configs.";
+ public static final String APOLLO_CLIENT_CONFIGMETA = "Apollo.Client.ConfigMeta";
+ public static final String APOLLO_CLIENT_NAMESPACE_NOT_FOUND = "Apollo.Client.NamespaceNotFound";
+ public static final String APOLLO_CLIENT_NAMESPACE_TIMEOUT = "Apollo.Client.NamespaceTimeout";
+ public static final String APOLLO_CLIENT_NAMESPACE_USAGE = "Apollo.Client.NamespaceUsage";
+ public static final String APOLLO_CLIENT_NAMESPACE_FIRST_LOAD_SPEND = "Apollo.Client.NamespaceFirstLoadSpendTime";
+
+ /**
+ * listener tag
+ */
+ public static final String TAG_ERROR = "ErrorMonitor";
+ public static final String TAG_NAMESPACE = "NamespaceMonitor";
+ public static final String TAG_BOOTSTRAP = "BootstrapMonitor";
+ public static final String TAG_THREAD_POOL = "ThreadPoolMonitor";
+
+ /**
+ * metrics
+ */
+ public static final String METRICS_NAMESPACE_LATEST_UPDATE_TIME = "apollo_client_namespace_latest_update_time";
+ public static final String METRICS_NAMESPACE_ITEM_NUM = "apollo_client_namespace_item_num";
+ public static final String METRICS_EXCEPTION_NUM = "apollo_client_exception_num";
+ public static final String METRICS_NAMESPACE_FIRST_LOAD_SPEND = "apollo_client_namespace_first_load_time_spend_in_ms";
+ public static final String METRICS_NAMESPACE_USAGE = "apollo_client_namespace_usage";
+ public static final String METRICS_NAMESPACE_NOT_FOUND = "apollo_client_namespace_not_found";
+ public static final String METRICS_NAMESPACE_TIMEOUT = "apollo_client_namespace_timeout";
+ public static final String METRICS_THREAD_POOL_NAME = "thread_pool_name";
+ public static final String METRICS_THREAD_POOL_ACTIVE_TASK_COUNT = "apollo_client_thread_pool_active_task_count";
+ public static final String METRICS_THREAD_POOL_QUEUE_SIZE = "apollo_client_thread_pool_queue_size";
+ public static final String METRICS_THREAD_POOL_COMPLETED_TASK_COUNT = "apollo_client_thread_pool_completed_task_count";
+ public static final String METRICS_THREAD_POOL_POOL_SIZE = "apollo_client_thread_pool_pool_size";
+ public static final String METRICS_THREAD_POOL_TOTAL_TASK_COUNT = "apollo_client_thread_pool_total_task_count";
+ public static final String METRICS_THREAD_POOL_CORE_POOL_SIZE = "apollo_client_thread_pool_core_pool_size";
+ public static final String METRICS_THREAD_POOL_MAXIMUM_POOL_SIZE = "apollo_client_thread_pool_maximum_pool_size";
+ public static final String METRICS_THREAD_POOL_LARGEST_POOL_SIZE = "apollo_client_thread_pool_largest_pool_size";
+ public static final String METRICS_THREAD_POOL_QUEUE_REMAINING_CAPACITY = "apollo_client_thread_pool_queue_remaining_capacity";
+}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/ApolloClientMonitorContext.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/ApolloClientMonitorContext.java
new file mode 100644
index 00000000..dbd3d671
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/ApolloClientMonitorContext.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal;
+
+import com.ctrip.framework.apollo.monitor.api.ApolloClientBootstrapArgsMonitorApi;
+import com.ctrip.framework.apollo.monitor.api.ApolloClientExceptionMonitorApi;
+import com.ctrip.framework.apollo.monitor.api.ApolloClientNamespaceMonitorApi;
+import com.ctrip.framework.apollo.monitor.api.ApolloClientThreadPoolMonitorApi;
+import com.ctrip.framework.apollo.monitor.internal.exporter.ApolloClientMetricsExporter;
+import com.ctrip.framework.apollo.monitor.internal.exporter.impl.NullApolloClientMetricsExporter;
+import com.ctrip.framework.apollo.monitor.internal.listener.ApolloClientMonitorEventListener;
+import com.ctrip.framework.apollo.monitor.internal.listener.impl.NullClientBootstrapArgsMonitorApi;
+import com.ctrip.framework.apollo.monitor.internal.listener.impl.NullClientExceptionMonitorApi;
+import com.ctrip.framework.apollo.monitor.internal.listener.impl.NullClientNamespaceMonitorApi;
+import com.ctrip.framework.apollo.monitor.internal.listener.impl.NullClientThreadPoolMonitorApi;
+import com.google.common.collect.Lists;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author Rawven
+ */
+public class ApolloClientMonitorContext {
+
+ private ApolloClientExceptionMonitorApi apolloClientExceptionMonitorApi = new NullClientExceptionMonitorApi();
+ private ApolloClientNamespaceMonitorApi apolloClientNamespaceMonitorApi = new NullClientNamespaceMonitorApi();
+ private ApolloClientBootstrapArgsMonitorApi apolloClientBootstrapArgsMonitorApi = new NullClientBootstrapArgsMonitorApi();
+ private ApolloClientThreadPoolMonitorApi apolloClientThreadPoolMonitorApi = new NullClientThreadPoolMonitorApi();
+ private ApolloClientMetricsExporter apolloClientMetricsExporter = new NullApolloClientMetricsExporter();
+ private List listener = Lists.newArrayList();
+
+ public void addApolloClientMonitorEventListener(ApolloClientMonitorEventListener listener) {
+ this.listener.add(listener);
+ }
+
+ public List getApolloClientMonitorEventListeners() {
+ return Collections.unmodifiableList(listener);
+ }
+
+ public void setApolloClientMonitorEventListeners(
+ List listeners) {
+ listener = listeners;
+ }
+
+ public void setApolloClientExceptionMonitorApi(
+ ApolloClientExceptionMonitorApi apolloClientExceptionMonitorApi) {
+ this.apolloClientExceptionMonitorApi = apolloClientExceptionMonitorApi;
+ }
+
+ public void setApolloClientNamespaceMonitorApi(
+ ApolloClientNamespaceMonitorApi apolloClientNamespaceMonitorApi) {
+ this.apolloClientNamespaceMonitorApi = apolloClientNamespaceMonitorApi;
+ }
+
+ public void setApolloClientBootstrapArgsMonitorApi(
+ ApolloClientBootstrapArgsMonitorApi apolloClientBootstrapArgsMonitorApi) {
+ this.apolloClientBootstrapArgsMonitorApi = apolloClientBootstrapArgsMonitorApi;
+ }
+
+ public void setApolloClientThreadPoolMonitorApi(
+ ApolloClientThreadPoolMonitorApi apolloClientThreadPoolMonitorApi) {
+ this.apolloClientThreadPoolMonitorApi = apolloClientThreadPoolMonitorApi;
+ }
+
+ public void setApolloClientMetricsExporter(
+ ApolloClientMetricsExporter apolloClientMetricsExporter) {
+ this.apolloClientMetricsExporter = apolloClientMetricsExporter;
+ }
+
+ public ApolloClientExceptionMonitorApi getExceptionApi() {
+ return apolloClientExceptionMonitorApi;
+ }
+
+ public ApolloClientNamespaceMonitorApi getNamespaceApi() {
+ return apolloClientNamespaceMonitorApi;
+ }
+
+ public ApolloClientBootstrapArgsMonitorApi getBootstrapArgsApi() {
+ return apolloClientBootstrapArgsMonitorApi;
+ }
+
+ public ApolloClientThreadPoolMonitorApi getThreadPoolApi() {
+ return apolloClientThreadPoolMonitorApi;
+ }
+
+ public ApolloClientMetricsExporter getMetricsExporter() {
+ return apolloClientMetricsExporter;
+ }
+}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/DefaultConfigMonitor.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/DefaultConfigMonitor.java
new file mode 100644
index 00000000..27b6606c
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/DefaultConfigMonitor.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal;
+
+import com.ctrip.framework.apollo.build.ApolloInjector;
+import com.ctrip.framework.apollo.monitor.api.ApolloClientBootstrapArgsMonitorApi;
+import com.ctrip.framework.apollo.monitor.api.ApolloClientExceptionMonitorApi;
+import com.ctrip.framework.apollo.monitor.api.ApolloClientNamespaceMonitorApi;
+import com.ctrip.framework.apollo.monitor.api.ApolloClientThreadPoolMonitorApi;
+import com.ctrip.framework.apollo.monitor.api.ConfigMonitor;
+
+/**
+ * exposes all collected data through ConfigService
+ *
+ * @author Rawven
+ */
+public class DefaultConfigMonitor implements ConfigMonitor {
+
+ private final ApolloClientMonitorContext apolloClientMonitorContext = ApolloInjector.getInstance(
+ ApolloClientMonitorContext.class);
+
+ @Override
+ public ApolloClientThreadPoolMonitorApi getThreadPoolMonitorApi() {
+ return apolloClientMonitorContext.getThreadPoolApi();
+ }
+
+ @Override
+ public ApolloClientExceptionMonitorApi getExceptionMonitorApi() {
+ return apolloClientMonitorContext.getExceptionApi();
+ }
+
+ @Override
+ public ApolloClientNamespaceMonitorApi getNamespaceMonitorApi() {
+ return apolloClientMonitorContext.getNamespaceApi();
+ }
+
+ @Override
+ public ApolloClientBootstrapArgsMonitorApi getRunningParamsMonitorApi() {
+ return apolloClientMonitorContext.getBootstrapArgsApi();
+ }
+
+ @Override
+ public String getExporterData() {
+ return apolloClientMonitorContext.getMetricsExporter().response();
+ }
+}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/enums/MetricTypeEnums.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/enums/MetricTypeEnums.java
new file mode 100644
index 00000000..c4c73440
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/enums/MetricTypeEnums.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.enums;
+
+/**
+ * @author Rawven
+ */
+public enum MetricTypeEnums {
+ /**
+ * counter
+ */
+ COUNTER,
+
+ /**
+ * gauge
+ */
+ GAUGE
+}
\ No newline at end of file
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/event/ApolloClientMonitorEvent.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/event/ApolloClientMonitorEvent.java
new file mode 100644
index 00000000..e7b4f077
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/event/ApolloClientMonitorEvent.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.event;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Rawven
+ */
+public class ApolloClientMonitorEvent {
+
+ private final String name;
+ private final Map attachments;
+ private String tag;
+
+ public ApolloClientMonitorEvent(String name, String tag, Map attachments) {
+ this.name = name;
+ this.tag = tag;
+ this.attachments = attachments != null ? new HashMap<>(attachments) : Collections.emptyMap();
+ }
+
+ public ApolloClientMonitorEvent withTag(String tag) {
+ this.tag = tag;
+ return this;
+ }
+
+ public String getTag() {
+ return tag;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public ApolloClientMonitorEvent putAttachment(String key, Object value) {
+ this.attachments.put(key, value);
+ return this;
+ }
+
+
+ @SuppressWarnings("unchecked")
+ public T getAttachmentValue(String key) {
+ Object value = attachments.get(key);
+ if (value == null) {
+ return null;
+ }
+ try {
+ return (T) value;
+ } catch (ClassCastException e) {
+ throw new IllegalArgumentException("Value for key " + key + " is not of expected type", e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/event/ApolloClientMonitorEventFactory.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/event/ApolloClientMonitorEventFactory.java
new file mode 100644
index 00000000..0bd51ecf
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/event/ApolloClientMonitorEventFactory.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.event;
+
+import java.util.HashMap;
+
+/**
+ * @author Rawven
+ * @date 2024/08/08
+ */
+public class ApolloClientMonitorEventFactory {
+
+ private static volatile ApolloClientMonitorEventFactory INSTANCE;
+
+ private ApolloClientMonitorEventFactory() {
+ }
+
+ public static ApolloClientMonitorEventFactory getInstance() {
+ if (INSTANCE == null) {
+ synchronized (ApolloClientMonitorEventFactory.class) {
+ if (INSTANCE == null) {
+ INSTANCE = new ApolloClientMonitorEventFactory();
+ }
+ }
+ }
+ return INSTANCE;
+ }
+
+ public ApolloClientMonitorEvent createEvent(String name) {
+ return new ApolloClientMonitorEvent(name, null, new HashMap<>(2));
+ }
+}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/event/ApolloClientMonitorEventPublisher.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/event/ApolloClientMonitorEventPublisher.java
new file mode 100644
index 00000000..7e11b0bb
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/event/ApolloClientMonitorEventPublisher.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.event;
+
+import com.ctrip.framework.apollo.build.ApolloInjector;
+import com.ctrip.framework.apollo.monitor.internal.listener.ApolloClientMonitorEventListener;
+import com.ctrip.framework.apollo.monitor.internal.ApolloClientMonitorContext;
+import com.ctrip.framework.apollo.util.ConfigUtil;
+
+/**
+ * @author Rawven
+ */
+public class ApolloClientMonitorEventPublisher {
+
+ private static ApolloClientMonitorContext MONITOR_CONTEXT = ApolloInjector.getInstance(
+ ApolloClientMonitorContext.class);
+ private static ConfigUtil m_configUtil = ApolloInjector.getInstance(ConfigUtil.class);
+
+ public static void publish(ApolloClientMonitorEvent event) {
+ if (m_configUtil.isClientMonitorEnabled()) {
+ for (ApolloClientMonitorEventListener listener : MONITOR_CONTEXT.getApolloClientMonitorEventListeners()) {
+ if (listener.isSupported(event)) {
+ listener.collect(event);
+ return;
+ }
+ }
+ }
+ }
+
+ protected static void reset() {
+ MONITOR_CONTEXT = ApolloInjector.getInstance(
+ ApolloClientMonitorContext.class);
+ m_configUtil = ApolloInjector.getInstance(ConfigUtil.class);
+
+ }
+}
+
+
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/exporter/AbstractApolloClientMetricsExporter.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/exporter/AbstractApolloClientMetricsExporter.java
new file mode 100644
index 00000000..0102d8cd
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/exporter/AbstractApolloClientMetricsExporter.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.exporter;
+
+import com.ctrip.framework.apollo.core.utils.ApolloThreadFactory;
+import com.ctrip.framework.apollo.core.utils.DeferredLoggerFactory;
+import com.ctrip.framework.apollo.monitor.internal.listener.ApolloClientMonitorEventListener;
+import com.ctrip.framework.apollo.monitor.internal.model.CounterModel;
+import com.ctrip.framework.apollo.monitor.internal.model.GaugeModel;
+import com.ctrip.framework.apollo.monitor.internal.model.SampleModel;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import org.slf4j.Logger;
+
+/**
+ * @author Rawven
+ */
+public abstract class AbstractApolloClientMetricsExporter implements ApolloClientMetricsExporter {
+
+ public static final ScheduledExecutorService m_executorService;
+ private static final Logger log = DeferredLoggerFactory.getLogger(
+ AbstractApolloClientMetricsExporter.class);
+ private static final long INITIAL_DELAY = 5L;
+ private static final int THREAD_POOL_SIZE = 1;
+
+ static {
+ m_executorService = Executors.newScheduledThreadPool(THREAD_POOL_SIZE,
+ ApolloThreadFactory.create(ApolloClientMetricsExporter.class.getName(), true));
+ }
+
+ protected List listeners;
+
+ @Override
+ public void init(List listeners, long collectPeriod) {
+ log.info("Initializing metrics exporter with {} listeners and collect period of {} seconds.",
+ listeners.size(), collectPeriod);
+ doInit();
+ this.listeners = listeners;
+ initScheduleMetricsCollectSync(collectPeriod);
+ log.info("Metrics collection scheduled with a period of {} seconds.", collectPeriod);
+ }
+
+ /**
+ * Provide initialization extension points
+ */
+ protected abstract void doInit();
+
+ private void initScheduleMetricsCollectSync(long collectPeriod) {
+ m_executorService.scheduleAtFixedRate(() -> {
+ try {
+ updateMetricsData();
+ } catch (Throwable ex) {
+ log.error("Error updating metrics data", ex);
+ }
+ }, INITIAL_DELAY, collectPeriod, TimeUnit.SECONDS);
+ }
+
+ protected void updateMetricsData() {
+ log.debug("Start to update metrics data job");
+ listeners.forEach(listener -> {
+ if (listener.isMetricsSampleUpdated()) {
+ log.debug("Listener {} has updated samples.", listener.getName());
+ listener.export().forEach(this::registerSample);
+ }
+ });
+ }
+
+ protected void registerSample(SampleModel sample) {
+ try {
+ switch (sample.getType()) {
+ case GAUGE:
+ GaugeModel gaugeModel = (GaugeModel) sample;
+ registerOrUpdateGaugeSample(gaugeModel.getName(), gaugeModel.getTags(),
+ gaugeModel.getValue());
+ break;
+ case COUNTER:
+ CounterModel counterModel = (CounterModel) sample;
+ registerOrUpdateCounterSample(counterModel.getName(), counterModel.getTags(),
+ counterModel.getIncreaseValueAndResetZero());
+ break;
+ default:
+ log.warn("Unsupported sample type: {}", sample.getType());
+ break;
+ }
+ } catch (Exception e) {
+ log.error("Register sample error", e);
+ }
+ }
+
+}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/exporter/ApolloClientMetricsExporter.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/exporter/ApolloClientMetricsExporter.java
new file mode 100644
index 00000000..83f32b5c
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/exporter/ApolloClientMetricsExporter.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.exporter;
+
+import com.ctrip.framework.apollo.core.spi.Ordered;
+import com.ctrip.framework.apollo.monitor.internal.listener.ApolloClientMonitorEventListener;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Rawven
+ */
+public interface ApolloClientMetricsExporter extends Ordered {
+
+ /**
+ * init method
+ */
+ void init(List listeners, long collectPeriod);
+
+ /**
+ * Used to access custom monitoring systems
+ */
+ boolean isSupport(String form);
+
+ /**
+ * register or update counter sample
+ */
+ void registerOrUpdateCounterSample(String name, Map tag, double incrValue);
+
+
+ /**
+ * register or update gauge sample
+ */
+ void registerOrUpdateGaugeSample(String name, Map tag, double value);
+
+ /**
+ * result of the collect metrics
+ */
+ String response();
+
+ @Override
+ default int getOrder() {
+ return 0;
+ }
+}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/exporter/ApolloClientMetricsExporterFactory.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/exporter/ApolloClientMetricsExporterFactory.java
new file mode 100644
index 00000000..cec6b6d8
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/exporter/ApolloClientMetricsExporterFactory.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.exporter;
+
+import com.ctrip.framework.apollo.monitor.internal.listener.ApolloClientMonitorEventListener;
+import java.util.List;
+
+/**
+ * @author Rawven
+ */
+public interface ApolloClientMetricsExporterFactory {
+
+ /**
+ * get metrics reporter
+ */
+ ApolloClientMetricsExporter getMetricsReporter(List listeners);
+}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/exporter/impl/DefaultApolloClientMetricsExporterFactory.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/exporter/impl/DefaultApolloClientMetricsExporterFactory.java
new file mode 100644
index 00000000..def890f3
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/exporter/impl/DefaultApolloClientMetricsExporterFactory.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.exporter.impl;
+
+import com.ctrip.framework.apollo.build.ApolloInjector;
+import com.ctrip.framework.apollo.core.utils.DeferredLoggerFactory;
+import com.ctrip.framework.apollo.core.utils.StringUtils;
+import com.ctrip.framework.apollo.exceptions.ApolloConfigException;
+import com.ctrip.framework.apollo.monitor.internal.listener.ApolloClientMonitorEventListener;
+import com.ctrip.framework.apollo.monitor.internal.exporter.ApolloClientMetricsExporter;
+import com.ctrip.framework.apollo.monitor.internal.exporter.ApolloClientMetricsExporterFactory;
+import com.ctrip.framework.apollo.tracer.Tracer;
+import com.ctrip.framework.apollo.util.ConfigUtil;
+import com.ctrip.framework.foundation.internals.ServiceBootstrap;
+import java.util.List;
+import org.slf4j.Logger;
+
+/**
+ * @author Rawven
+ */
+public class DefaultApolloClientMetricsExporterFactory implements
+ ApolloClientMetricsExporterFactory {
+
+ private static final Logger logger = DeferredLoggerFactory.getLogger(
+ DefaultApolloClientMetricsExporterFactory.class);
+ private final ConfigUtil configUtil = ApolloInjector.getInstance(ConfigUtil.class);
+
+ @Override
+ public ApolloClientMetricsExporter getMetricsReporter(
+ List listeners) {
+ String externalSystemType = configUtil.getMonitorExternalType();
+ return findAndInitializeExporter(listeners, externalSystemType);
+ }
+
+ private ApolloClientMetricsExporter findAndInitializeExporter(
+ List listeners, String externalSystemType) {
+ List exporters = ServiceBootstrap.loadAllOrdered(
+ ApolloClientMetricsExporter.class);
+ if (StringUtils.isEmpty(externalSystemType)) {
+ return null;
+ }
+ ApolloClientMetricsExporter reporter = exporters.stream()
+ .filter(metricsExporter -> metricsExporter.isSupport(externalSystemType))
+ .findFirst()
+ .orElse(null);
+
+ if (reporter != null) {
+ reporter.init(listeners, configUtil.getMonitorExternalExportPeriod());
+ } else {
+ String errorMessage =
+ "No matching exporter found with monitor-external-type " + externalSystemType;
+ ApolloConfigException exception = new ApolloConfigException(errorMessage);
+ logger.error(
+ "Error initializing exporter for external-type: {}. Please check if external-type is misspelled or the correct dependency is not introduced, such as apollo-plugin-client-prometheus",
+ externalSystemType, exception);
+ Tracer.logError(exception);
+ }
+ return reporter;
+ }
+
+}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/exporter/impl/NullApolloClientMetricsExporter.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/exporter/impl/NullApolloClientMetricsExporter.java
new file mode 100644
index 00000000..3bc195d5
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/exporter/impl/NullApolloClientMetricsExporter.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.exporter.impl;
+
+import com.ctrip.framework.apollo.core.utils.DeferredLoggerFactory;
+import com.ctrip.framework.apollo.monitor.internal.exporter.AbstractApolloClientMetricsExporter;
+import com.ctrip.framework.apollo.monitor.internal.listener.ApolloClientMonitorEventListener;
+import com.ctrip.framework.apollo.monitor.internal.exporter.ApolloClientMetricsExporter;
+import java.util.List;
+import java.util.Map;
+import org.slf4j.Logger;
+
+/**
+ * @author Rawven
+ */
+public class NullApolloClientMetricsExporter implements ApolloClientMetricsExporter {
+
+ private static final Logger log = DeferredLoggerFactory.getLogger(
+ NullApolloClientMetricsExporter.class);
+
+ @Override
+ public void init(List listeners, long collectPeriod) {
+ }
+
+ @Override
+ public boolean isSupport(String form) {
+ return false;
+ }
+
+ @Override
+ public void registerOrUpdateCounterSample(String name, Map tag,
+ double incrValue) {
+
+ }
+
+ @Override
+ public void registerOrUpdateGaugeSample(String name, Map tag, double value) {
+
+ }
+
+ @Override
+ public String response() {
+ log.warn("No metrics exporter found, response empty string");
+ return "";
+ }
+}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/jmx/ApolloClientJmxMBeanRegister.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/jmx/ApolloClientJmxMBeanRegister.java
new file mode 100644
index 00000000..33d9108d
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/jmx/ApolloClientJmxMBeanRegister.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.jmx;
+
+import static com.ctrip.framework.apollo.monitor.internal.ApolloClientMonitorConstant.MBEAN_NAME;
+
+import com.ctrip.framework.apollo.core.utils.DeferredLoggerFactory;
+import java.lang.management.ManagementFactory;
+import javax.management.JMException;
+import javax.management.MBeanServer;
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+import org.slf4j.Logger;
+
+/**
+ * @author Rawven
+ */
+public final class ApolloClientJmxMBeanRegister {
+
+ private static final Logger logger = DeferredLoggerFactory.getLogger(
+ ApolloClientJmxMBeanRegister.class);
+ private static MBeanServer mbeanServer;
+ private static ObjectName ERROR_OBJECT_NAME;
+
+ static {
+ try {
+ ERROR_OBJECT_NAME = new ObjectName(MBEAN_NAME + "error");
+ } catch (MalformedObjectNameException e) {
+ logger.warn("MalformedObjectNameException during static initialization.", e);
+ }
+ }
+
+ public static synchronized void setMBeanServer(MBeanServer mbeanServer) {
+ ApolloClientJmxMBeanRegister.mbeanServer = mbeanServer;
+ }
+
+ public static synchronized ObjectName register(String name, Object mbean) {
+ try {
+ ObjectName objectName = new ObjectName(name);
+
+ if (mbeanServer == null) {
+ mbeanServer = ManagementFactory.getPlatformMBeanServer();
+ }
+
+ if (mbeanServer.isRegistered(objectName)) {
+ mbeanServer.unregisterMBean(objectName);
+ }
+ mbeanServer.registerMBean(mbean, objectName);
+
+ return objectName;
+ } catch (JMException e) {
+ logger.error("Register JMX MBean failed.", e);
+ return ERROR_OBJECT_NAME;
+ }
+ }
+
+ public static synchronized void unregister(String name) {
+ try {
+ MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
+ mbeanServer.unregisterMBean(new ObjectName(name));
+ } catch (JMException e) {
+ logger.error("Unregister JMX MBean failed.", e);
+ }
+ }
+}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/jmx/mbean/ApolloClientJmxBootstrapArgsMBean.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/jmx/mbean/ApolloClientJmxBootstrapArgsMBean.java
new file mode 100644
index 00000000..d00597b5
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/jmx/mbean/ApolloClientJmxBootstrapArgsMBean.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.jmx.mbean;
+
+import java.util.Map;
+import javax.management.MXBean;
+
+/**
+ * @author Rawven
+ */
+@MXBean
+public interface ApolloClientJmxBootstrapArgsMBean {
+ // Because JMX does not support all type return values
+ // declare the interface separately.
+
+ /**
+ * get bootstrap args map
+ */
+ Map getBootstrapArgsString();
+
+}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/jmx/mbean/ApolloClientJmxExceptionMBean.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/jmx/mbean/ApolloClientJmxExceptionMBean.java
new file mode 100644
index 00000000..076b8639
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/jmx/mbean/ApolloClientJmxExceptionMBean.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.jmx.mbean;
+
+import java.util.List;
+import javax.management.MXBean;
+
+/**
+ * @author Rawven
+ */
+@MXBean
+public interface ApolloClientJmxExceptionMBean {
+ // Because JMX does not support all type return values
+ // declare the interface separately.
+
+ /**
+ * get exception details
+ */
+ List getApolloConfigExceptionDetails();
+
+ Integer getExceptionCountFromStartup();
+}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/jmx/mbean/ApolloClientJmxNamespaceMBean.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/jmx/mbean/ApolloClientJmxNamespaceMBean.java
new file mode 100644
index 00000000..e2a9412a
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/jmx/mbean/ApolloClientJmxNamespaceMBean.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.jmx.mbean;
+
+import java.util.List;
+import java.util.Map;
+import javax.management.MXBean;
+
+/**
+ * @author Rawven
+ */
+@MXBean
+public interface ApolloClientJmxNamespaceMBean {
+ // Because JMX does not support all type return values
+ // declare the interface separately.
+
+ /**
+ * NamespaceMetrics: 1.usageCount 2.firstLoadSpend 3.latestUpdateTime 4.releaseKey
+ */
+ Map getNamespaceMetricsString();
+
+ /**
+ * get Namespace Config.ItemsNum
+ */
+ Integer getNamespacePropertySize(String namespace);
+
+ /**
+ * get not found namespaces
+ */
+ List getNotFoundNamespaces();
+
+ /**
+ * get timeout namespaces
+ */
+ List getTimeoutNamespaces();
+
+
+ class NamespaceMetricsString {
+
+ private int usageCount;
+ private long firstLoadTimeSpendInMs;
+ private String latestUpdateTime;
+ private String releaseKey = "";
+
+ public int getUsageCount() {
+ return usageCount;
+ }
+
+ public void setUsageCount(int usageCount) {
+ this.usageCount = usageCount;
+ }
+
+ public long getFirstLoadTimeSpendInMs() {
+ return firstLoadTimeSpendInMs;
+ }
+
+ public void setFirstLoadTimeSpendInMs(long firstLoadTimeSpendInMs) {
+ this.firstLoadTimeSpendInMs = firstLoadTimeSpendInMs;
+ }
+
+ public String getLatestUpdateTime() {
+ return latestUpdateTime;
+ }
+
+ public void setLatestUpdateTime(String latestUpdateTime) {
+ this.latestUpdateTime = latestUpdateTime;
+ }
+
+ public String getReleaseKey() {
+ return releaseKey;
+ }
+
+ public void setReleaseKey(String releaseKey) {
+ this.releaseKey = releaseKey;
+ }
+ }
+
+}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/jmx/mbean/ApolloClientJmxThreadPoolMBean.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/jmx/mbean/ApolloClientJmxThreadPoolMBean.java
new file mode 100644
index 00000000..78d090fa
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/jmx/mbean/ApolloClientJmxThreadPoolMBean.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.jmx.mbean;
+
+import com.ctrip.framework.apollo.monitor.api.ApolloClientThreadPoolMonitorApi;
+import javax.management.MXBean;
+
+/**
+ * @author Rawven
+ */
+@MXBean
+public interface ApolloClientJmxThreadPoolMBean extends ApolloClientThreadPoolMonitorApi {
+
+
+}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/listener/AbstractApolloClientMonitorEventListener.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/listener/AbstractApolloClientMonitorEventListener.java
new file mode 100644
index 00000000..847410f7
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/listener/AbstractApolloClientMonitorEventListener.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.listener;
+
+import com.ctrip.framework.apollo.monitor.internal.event.ApolloClientMonitorEvent;
+import com.ctrip.framework.apollo.monitor.internal.model.CounterModel;
+import com.ctrip.framework.apollo.monitor.internal.model.GaugeModel;
+import com.ctrip.framework.apollo.monitor.internal.model.SampleModel;
+import com.google.common.collect.Maps;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * @author Rawven
+ */
+public abstract class AbstractApolloClientMonitorEventListener implements
+ ApolloClientMonitorEventListener {
+
+ private final Map counterSamples = Maps.newConcurrentMap();
+ private final Map gaugeSamples = Maps.newConcurrentMap();
+ private final AtomicBoolean isUpdated = new AtomicBoolean();
+ private final String tag;
+
+ public AbstractApolloClientMonitorEventListener(String tag) {
+ this.tag = tag;
+ }
+
+ @Override
+ public String getName() {
+ return tag;
+ }
+
+ @Override
+ public boolean isSupported(ApolloClientMonitorEvent event) {
+ return tag.equals(event.getTag());
+ }
+
+ @Override
+ public void collect(ApolloClientMonitorEvent event) {
+ collect0(event);
+ isUpdated.set(true);
+ }
+
+ @Override
+ public boolean isMetricsSampleUpdated() {
+ return isUpdated.getAndSet(false);
+ }
+
+ @Override
+ public List export() {
+ export0();
+ List samples = new ArrayList<>(counterSamples.values());
+ samples.addAll(gaugeSamples.values());
+ return samples;
+ }
+
+ /**
+ * Specific collection logic
+ */
+ protected void collect0(ApolloClientMonitorEvent event) {
+ }
+
+ /**
+ * Convenient for indicators that can only be obtained from the status object
+ */
+ protected void export0() {
+ }
+
+
+ /**
+ * tool method for updating indicator model
+ */
+ public void createOrUpdateGaugeSample(String metricsName, String[] tagKeys, String[] tagValues,
+ double value) {
+ createOrUpdateSample(metricsName, tagKeys, tagValues, value, false);
+ }
+
+ public void createOrUpdateGaugeSample(String metricsName, double value) {
+ createOrUpdateSample(metricsName, null, null, value, false);
+ }
+
+ public void createOrUpdateCounterSample(String metricsName, String[] tagKeys, String[] tagValues,
+ double increaseValue) {
+ createOrUpdateSample(metricsName, tagKeys, tagValues, increaseValue, true);
+ }
+
+ public void createOrUpdateCounterSample(String metricsName, double increaseValue) {
+ createOrUpdateSample(metricsName, null, null, increaseValue, true);
+ }
+
+ private void createOrUpdateSample(String metricsName, String[] tagKeys, String[] tagValues,
+ double value, boolean isCounter) {
+ String mapKey = metricsName + (tagValues != null ? Arrays.toString(tagValues) : "");
+
+ if (isCounter) {
+ CounterModel counter = counterSamples.computeIfAbsent(mapKey,
+ key -> (CounterModel) CounterModel.create(metricsName, 0)
+ .putTags(getTags(tagKeys, tagValues)));
+ counter.increase(value);
+ } else {
+ GaugeModel gauge = gaugeSamples.computeIfAbsent(mapKey,
+ key -> (GaugeModel) GaugeModel.create(metricsName, 0)
+ .putTags(getTags(tagKeys, tagValues)));
+ gauge.setValue(value);
+ }
+ }
+
+ private Map getTags(String[] tagKeys, String[] tagValues) {
+ if (tagKeys != null && tagValues != null && tagKeys.length == tagValues.length) {
+ Map tags = Maps.newHashMap();
+ for (int i = 0; i < tagKeys.length; i++) {
+ tags.put(tagKeys[i], tagValues[i]);
+ }
+ return tags;
+ }
+ return Collections.emptyMap();
+ }
+
+}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/listener/ApolloClientMonitorEventListener.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/listener/ApolloClientMonitorEventListener.java
new file mode 100644
index 00000000..d808d24d
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/listener/ApolloClientMonitorEventListener.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.listener;
+
+import com.ctrip.framework.apollo.monitor.internal.event.ApolloClientMonitorEvent;
+import com.ctrip.framework.apollo.monitor.internal.model.SampleModel;
+import java.util.List;
+
+/**
+ * @author Rawven
+ */
+public interface ApolloClientMonitorEventListener {
+
+
+ /**
+ * mbean name
+ */
+ String getName();
+
+ /**
+ * is support the event
+ */
+ boolean isSupported(ApolloClientMonitorEvent event);
+
+ /**
+ * collect metrics from event
+ */
+ void collect(ApolloClientMonitorEvent event);
+
+ /**
+ * is samples updated
+ */
+ boolean isMetricsSampleUpdated();
+
+ /**
+ * export to a format recognized by the monitoring system
+ */
+ List export();
+
+
+}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/DefaultApolloClientBootstrapArgsApi.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/DefaultApolloClientBootstrapArgsApi.java
new file mode 100644
index 00000000..19ca9b30
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/DefaultApolloClientBootstrapArgsApi.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.listener.impl;
+
+import static com.ctrip.framework.apollo.core.ApolloClientSystemConsts.*;
+import static com.ctrip.framework.apollo.core.ConfigConsts.APOLLO_AUTO_UPDATE_INJECTED_SPRING_PROPERTIES;
+import static com.ctrip.framework.apollo.monitor.internal.ApolloClientMonitorConstant.*;
+import static com.ctrip.framework.apollo.spring.config.PropertySourcesConstants.*;
+
+import com.ctrip.framework.apollo.Apollo;
+import com.ctrip.framework.apollo.core.utils.DeferredLoggerFactory;
+import com.ctrip.framework.apollo.monitor.api.ApolloClientBootstrapArgsMonitorApi;
+import com.ctrip.framework.apollo.monitor.internal.jmx.mbean.ApolloClientJmxBootstrapArgsMBean;
+import com.ctrip.framework.apollo.monitor.internal.listener.AbstractApolloClientMonitorEventListener;
+import com.ctrip.framework.apollo.monitor.internal.event.ApolloClientMonitorEvent;
+import com.ctrip.framework.apollo.util.ConfigUtil;
+import com.google.common.collect.Maps;
+import java.util.Map;
+import org.slf4j.Logger;
+
+/**
+ * @author Rawven
+ */
+public class DefaultApolloClientBootstrapArgsApi extends
+ AbstractApolloClientMonitorEventListener implements
+ ApolloClientBootstrapArgsMonitorApi, ApolloClientJmxBootstrapArgsMBean {
+
+ private static final Logger logger = DeferredLoggerFactory.getLogger(
+ DefaultApolloClientBootstrapArgsApi.class);
+ private final Map bootstrapArgs = Maps.newHashMap();
+ private final Map bootstrapArgsString = Maps.newHashMap();
+
+ public DefaultApolloClientBootstrapArgsApi(ConfigUtil configUtil) {
+ super(TAG_BOOTSTRAP);
+ bootstrapArgs.put(APOLLO_ACCESS_KEY_SECRET, configUtil.getAccessKeySecret());
+ bootstrapArgs.put(APOLLO_AUTO_UPDATE_INJECTED_SPRING_PROPERTIES,
+ configUtil.isAutoUpdateInjectedSpringPropertiesEnabled());
+ bootstrapArgs.put(APOLLO_BOOTSTRAP_ENABLED,
+ Boolean.parseBoolean(System.getProperty(APOLLO_BOOTSTRAP_ENABLED)));
+ bootstrapArgs.put(APOLLO_BOOTSTRAP_NAMESPACES,
+ System.getProperty(APOLLO_BOOTSTRAP_NAMESPACES));
+ bootstrapArgs.put(APOLLO_BOOTSTRAP_EAGER_LOAD_ENABLED,
+ Boolean.parseBoolean(System.getProperty(APOLLO_BOOTSTRAP_EAGER_LOAD_ENABLED)));
+ bootstrapArgs.put(APOLLO_OVERRIDE_SYSTEM_PROPERTIES, configUtil.isOverrideSystemProperties());
+ bootstrapArgs.put(APOLLO_CACHE_DIR, configUtil.getDefaultLocalCacheDir());
+ bootstrapArgs.put(APOLLO_CLUSTER, configUtil.getCluster());
+ bootstrapArgs.put(APOLLO_CONFIG_SERVICE,
+ System.getProperty(APOLLO_CONFIG_SERVICE));
+ bootstrapArgs.put(APOLLO_CLIENT_MONITOR_EXTERNAL_TYPE, configUtil.getMonitorExternalType());
+ bootstrapArgs.put(APOLLO_CLIENT_MONITOR_ENABLED, configUtil.isClientMonitorEnabled());
+ bootstrapArgs.put(APOLLO_CLIENT_MONITOR_EXTERNAL_EXPORT_PERIOD,
+ configUtil.getMonitorExternalExportPeriod());
+ bootstrapArgs.put(APOLLO_META, configUtil.getMetaServerDomainName());
+ bootstrapArgs.put(APOLLO_PROPERTY_NAMES_CACHE_ENABLE, configUtil.isPropertyNamesCacheEnabled());
+ bootstrapArgs.put(APOLLO_PROPERTY_ORDER_ENABLE, configUtil.isPropertiesOrderEnabled());
+ bootstrapArgs.put(APOLLO_CLIENT_MONITOR_JMX_ENABLED, configUtil.isClientMonitorJmxEnabled());
+ bootstrapArgs.put(APOLLO_CLIENT_MONITOR_EXCEPTION_QUEUE_SIZE,
+ configUtil.getMonitorExceptionQueueSize());
+ bootstrapArgs.put(APP_ID, configUtil.getAppId());
+ bootstrapArgs.put(ENV, configUtil.getApolloEnv());
+ bootstrapArgs.put(VERSION, Apollo.VERSION);
+ bootstrapArgs.forEach((key, value) -> {
+ if (value != null) {
+ bootstrapArgsString.put(key, value.toString());
+ }
+ });
+
+ }
+
+ @Override
+ public void collect0(ApolloClientMonitorEvent event) {
+ String argName = event.getName();
+ if (bootstrapArgs.containsKey(argName)) {
+ bootstrapArgs.put(argName, event.getAttachmentValue(argName));
+ } else {
+ logger.warn("Unhandled event name: {}", argName);
+ }
+ }
+
+ @Override
+ public boolean isMetricsSampleUpdated() {
+ return false;
+ }
+
+ @Override
+ public Map getBootstrapArgs() {
+ return bootstrapArgs;
+ }
+
+ @Override
+ public Map getBootstrapArgsString() {
+ return bootstrapArgsString;
+ }
+}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/DefaultApolloClientExceptionApi.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/DefaultApolloClientExceptionApi.java
new file mode 100644
index 00000000..ba650a12
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/DefaultApolloClientExceptionApi.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.listener.impl;
+
+import static com.ctrip.framework.apollo.monitor.internal.ApolloClientMonitorConstant.METRICS_EXCEPTION_NUM;
+import static com.ctrip.framework.apollo.monitor.internal.ApolloClientMonitorConstant.TAG_ERROR;
+import static com.ctrip.framework.apollo.monitor.internal.ApolloClientMonitorConstant.THROWABLE;
+
+import com.ctrip.framework.apollo.build.ApolloInjector;
+import com.ctrip.framework.apollo.exceptions.ApolloConfigException;
+import com.ctrip.framework.apollo.monitor.api.ApolloClientExceptionMonitorApi;
+import com.ctrip.framework.apollo.monitor.internal.jmx.mbean.ApolloClientJmxExceptionMBean;
+import com.ctrip.framework.apollo.monitor.internal.listener.AbstractApolloClientMonitorEventListener;
+import com.ctrip.framework.apollo.monitor.internal.event.ApolloClientMonitorEvent;
+import com.ctrip.framework.apollo.util.ConfigUtil;
+import com.google.common.collect.EvictingQueue;
+import com.google.common.collect.Queues;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
+
+/**
+ * @author Rawven
+ */
+public class DefaultApolloClientExceptionApi extends
+ AbstractApolloClientMonitorEventListener implements
+ ApolloClientExceptionMonitorApi, ApolloClientJmxExceptionMBean {
+
+ private final AtomicInteger exceptionCountFromStartup = new AtomicInteger(0);
+ private final Queue exceptionsQueue;
+
+ public DefaultApolloClientExceptionApi(ConfigUtil configUtil) {
+ super(TAG_ERROR);
+ int monitorExceptionQueueSize = configUtil.getMonitorExceptionQueueSize();
+ EvictingQueue evictingQueue = EvictingQueue.create(
+ monitorExceptionQueueSize);
+ exceptionsQueue = Queues.synchronizedQueue(evictingQueue);
+ }
+
+ @Override
+ public List getApolloConfigExceptionList() {
+ return new ArrayList<>(exceptionsQueue);
+ }
+
+ @Override
+ public Integer getExceptionCountFromStartup() {
+ return exceptionCountFromStartup.get();
+ }
+
+ @Override
+ public void collect0(ApolloClientMonitorEvent event) {
+ ApolloConfigException exception = event.getAttachmentValue(THROWABLE);
+ if (exception != null) {
+ exceptionsQueue.add(exception);
+ exceptionCountFromStartup.incrementAndGet();
+ createOrUpdateCounterSample(METRICS_EXCEPTION_NUM, 1);
+ }
+ }
+
+ @Override
+ public List getApolloConfigExceptionDetails() {
+ return exceptionsQueue.stream()
+ .map(ApolloConfigException::getMessage)
+ .collect(Collectors.toList());
+ }
+}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/DefaultApolloClientNamespaceApi.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/DefaultApolloClientNamespaceApi.java
new file mode 100644
index 00000000..b6527b8b
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/DefaultApolloClientNamespaceApi.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.listener.impl;
+
+
+import static com.ctrip.framework.apollo.monitor.internal.ApolloClientMonitorConstant.*;
+
+import com.ctrip.framework.apollo.Config;
+import com.ctrip.framework.apollo.ConfigFile;
+import com.ctrip.framework.apollo.core.utils.DeferredLoggerFactory;
+import com.ctrip.framework.apollo.internals.ConfigManager;
+import com.ctrip.framework.apollo.monitor.api.ApolloClientNamespaceMonitorApi;
+import com.ctrip.framework.apollo.monitor.internal.jmx.mbean.ApolloClientJmxNamespaceMBean;
+import com.ctrip.framework.apollo.monitor.internal.ApolloClientMonitorConstant;
+import com.ctrip.framework.apollo.monitor.internal.listener.AbstractApolloClientMonitorEventListener;
+import com.ctrip.framework.apollo.monitor.internal.event.ApolloClientMonitorEvent;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.slf4j.Logger;
+
+/**
+ * @author Rawven
+ */
+public class DefaultApolloClientNamespaceApi extends
+ AbstractApolloClientMonitorEventListener implements
+ ApolloClientNamespaceMonitorApi, ApolloClientJmxNamespaceMBean {
+
+ private static final Logger logger = DeferredLoggerFactory.getLogger(
+ DefaultApolloClientNamespaceApi.class);
+ private final ConfigManager configManager;
+ private final Map namespaces = Maps.newConcurrentMap();
+ private final Set namespace404 = Sets.newCopyOnWriteArraySet();
+ private final Set namespaceTimeout = Sets.newCopyOnWriteArraySet();
+
+ public DefaultApolloClientNamespaceApi(ConfigManager configManager
+ ) {
+ super(TAG_NAMESPACE);
+ this.configManager = configManager;
+ }
+
+ @Override
+ public void collect0(ApolloClientMonitorEvent event) {
+ String namespace = event.getAttachmentValue(NAMESPACE);
+ String eventName = event.getName();
+
+ switch (eventName) {
+ case APOLLO_CLIENT_NAMESPACE_NOT_FOUND:
+ handleNamespaceNotFound(namespace);
+ break;
+ case APOLLO_CLIENT_NAMESPACE_TIMEOUT:
+ handleNamespaceTimeout(namespace);
+ break;
+ default:
+ handleNormalNamespace(namespace, event);
+ break;
+ }
+ }
+
+ private void handleNormalNamespace(String namespace, ApolloClientMonitorEvent event) {
+ namespace404.remove(namespace);
+ namespaceTimeout.remove(namespace);
+ NamespaceMetrics namespaceMetrics = namespaces.computeIfAbsent(namespace,
+ k -> new NamespaceMetrics());
+ collectMetrics(event, namespaceMetrics, namespace);
+ }
+
+ private void collectMetrics(ApolloClientMonitorEvent event, NamespaceMetrics namespaceMetrics,
+ String namespace) {
+ String eventName = event.getName();
+ switch (eventName) {
+ case APOLLO_CLIENT_NAMESPACE_USAGE:
+ handleUsageEvent(namespaceMetrics, namespace);
+ break;
+ case METRICS_NAMESPACE_LATEST_UPDATE_TIME:
+ handleUpdateTimeEvent(event, namespaceMetrics);
+ break;
+ case APOLLO_CLIENT_NAMESPACE_FIRST_LOAD_SPEND:
+ handleFirstLoadSpendEvent(event, namespaceMetrics);
+ break;
+ case NAMESPACE_RELEASE_KEY:
+ handleReleaseKeyEvent(event, namespaceMetrics);
+ break;
+ default:
+ logger.warn("Unhandled event name: {}", eventName);
+ break;
+ }
+ }
+
+ private void handleNamespaceNotFound(String namespace) {
+ namespace404.add(namespace);
+ }
+
+ private void handleNamespaceTimeout(String namespace) {
+ namespaceTimeout.add(namespace);
+ }
+
+
+ private void handleUsageEvent(NamespaceMetrics namespaceMetrics, String namespace) {
+ namespaceMetrics.incrementUsageCount();
+ createOrUpdateCounterSample(ApolloClientMonitorConstant.METRICS_NAMESPACE_USAGE,
+ new String[]{NAMESPACE}, new String[]{namespace}, 1);
+ }
+
+ private void handleUpdateTimeEvent(ApolloClientMonitorEvent event,
+ NamespaceMetrics namespaceMetrics) {
+ namespaceMetrics.setLatestUpdateTime(LocalDateTime.now());
+ }
+
+ private void handleFirstLoadSpendEvent(ApolloClientMonitorEvent event,
+ NamespaceMetrics namespaceMetrics) {
+ long firstLoadSpendTime = event.getAttachmentValue(ApolloClientMonitorConstant.TIMESTAMP);
+ namespaceMetrics.setFirstLoadTimeSpendInMs(firstLoadSpendTime);
+ }
+
+ private void handleReleaseKeyEvent(ApolloClientMonitorEvent event,
+ NamespaceMetrics namespaceMetrics) {
+ String releaseKey = event.getAttachmentValue(NAMESPACE_RELEASE_KEY);
+ namespaceMetrics.setReleaseKey(releaseKey);
+ }
+
+ @Override
+ public void export0() {
+ namespaces.forEach((namespace, metrics) -> {
+ // update NamespaceMetrics
+ createOrUpdateGaugeSample(
+ METRICS_NAMESPACE_FIRST_LOAD_SPEND,
+ new String[]{NAMESPACE}, new String[]{namespace},
+ metrics.getFirstLoadTimeSpendInMs());
+
+ createOrUpdateGaugeSample(
+ METRICS_NAMESPACE_ITEM_NUM,
+ new String[]{NAMESPACE}, new String[]{namespace},
+ configManager.getConfig(namespace).getPropertyNames().size());
+ });
+
+ // update NamespaceStatus metrics
+ createOrUpdateGaugeSample(METRICS_NAMESPACE_NOT_FOUND,
+ namespace404.size());
+
+ createOrUpdateGaugeSample(METRICS_NAMESPACE_TIMEOUT,
+ namespaceTimeout.size());
+ }
+
+ @Override
+ public Map getNamespaceMetrics() {
+ return Collections.unmodifiableMap(namespaces);
+ }
+
+ @Override
+ public List getNotFoundNamespaces() {
+ return new ArrayList<>(namespace404);
+ }
+
+ @Override
+ public List getTimeoutNamespaces() {
+ return new ArrayList<>(namespaceTimeout);
+ }
+
+ @Override
+ public Map getNamespaceMetricsString() {
+ Map namespaceMetricsStringMap = Maps.newHashMap();
+ namespaces.forEach((namespace, metrics) -> {
+ NamespaceMetricsString namespaceMetricsString = new NamespaceMetricsString();
+ namespaceMetricsString.setFirstLoadTimeSpendInMs(metrics.getFirstLoadTimeSpendInMs());
+ namespaceMetricsString.setLatestUpdateTime(metrics.getLatestUpdateTime().toString());
+ namespaceMetricsString.setUsageCount(metrics.getUsageCount());
+ namespaceMetricsString.setReleaseKey(metrics.getReleaseKey());
+ namespaceMetricsStringMap.put(namespace, namespaceMetricsString);
+ });
+ return namespaceMetricsStringMap;
+ }
+
+ @Override
+ public Integer getNamespacePropertySize(String namespace) {
+ Config config = configManager.getConfig(namespace);
+ return (config != null) ? config.getPropertyNames().size() : 0;
+ }
+
+}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/DefaultApolloClientThreadPoolApi.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/DefaultApolloClientThreadPoolApi.java
new file mode 100644
index 00000000..84316629
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/DefaultApolloClientThreadPoolApi.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.listener.impl;
+
+import static com.ctrip.framework.apollo.monitor.internal.ApolloClientMonitorConstant.*;
+
+import com.ctrip.framework.apollo.internals.AbstractConfig;
+import com.ctrip.framework.apollo.internals.AbstractConfigFile;
+import com.ctrip.framework.apollo.internals.RemoteConfigRepository;
+import com.ctrip.framework.apollo.monitor.api.ApolloClientThreadPoolMonitorApi;
+import com.ctrip.framework.apollo.monitor.internal.exporter.AbstractApolloClientMetricsExporter;
+import com.ctrip.framework.apollo.monitor.internal.jmx.mbean.ApolloClientJmxThreadPoolMBean;
+import com.ctrip.framework.apollo.monitor.internal.listener.AbstractApolloClientMonitorEventListener;
+import com.google.common.collect.Maps;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ThreadPoolExecutor;
+
+/**
+ * @author Rawven
+ */
+public class DefaultApolloClientThreadPoolApi extends
+ AbstractApolloClientMonitorEventListener implements
+ ApolloClientThreadPoolMonitorApi, ApolloClientJmxThreadPoolMBean {
+
+ public static final String REMOTE_CONFIG_REPOSITORY = RemoteConfigRepository.class.getSimpleName();
+ public static final String ABSTRACT_CONFIG = AbstractConfig.class.getSimpleName();
+ public static final String ABSTRACT_CONFIG_FILE = AbstractConfigFile.class.getSimpleName();
+ public static final String METRICS_EXPORTER = AbstractApolloClientMetricsExporter.class.getSimpleName();
+ private final Map executorMap = Maps.newHashMap();
+
+ public DefaultApolloClientThreadPoolApi(
+ ExecutorService remoteConfigRepositoryExecutorService,
+ ExecutorService abstractConfigExecutorService,
+ ExecutorService abstractConfigFileExecutorService,
+ ExecutorService metricsExporterExecutorService) {
+ super(TAG_THREAD_POOL);
+ executorMap.put(REMOTE_CONFIG_REPOSITORY,
+ new ApolloThreadPoolInfo((ThreadPoolExecutor) remoteConfigRepositoryExecutorService));
+ executorMap.put(ABSTRACT_CONFIG,
+ new ApolloThreadPoolInfo((ThreadPoolExecutor) abstractConfigExecutorService));
+ executorMap.put(ABSTRACT_CONFIG_FILE,
+ new ApolloThreadPoolInfo((ThreadPoolExecutor) abstractConfigFileExecutorService));
+ executorMap.put(METRICS_EXPORTER,
+ new ApolloThreadPoolInfo((ThreadPoolExecutor) metricsExporterExecutorService));
+ }
+
+ @Override
+ public void export0() {
+ executorMap.forEach((key, value) -> exportThreadPoolMetrics(value, key));
+ }
+
+ private void exportThreadPoolMetrics(ApolloThreadPoolInfo info, String threadPoolName) {
+
+ createOrUpdateGaugeSample(METRICS_THREAD_POOL_ACTIVE_TASK_COUNT,
+ new String[]{METRICS_THREAD_POOL_NAME}, new String[]{threadPoolName},
+ info.getActiveTaskCount());
+ createOrUpdateGaugeSample(METRICS_THREAD_POOL_QUEUE_SIZE,
+ new String[]{METRICS_THREAD_POOL_NAME}, new String[]{threadPoolName},
+ info.getQueueSize());
+ createOrUpdateGaugeSample(METRICS_THREAD_POOL_COMPLETED_TASK_COUNT,
+ new String[]{METRICS_THREAD_POOL_NAME}, new String[]{threadPoolName},
+ (double) info.getCompletedTaskCount());
+ createOrUpdateGaugeSample(METRICS_THREAD_POOL_POOL_SIZE, new String[]{METRICS_THREAD_POOL_NAME},
+ new String[]{threadPoolName}, info.getPoolSize());
+ createOrUpdateGaugeSample(METRICS_THREAD_POOL_TOTAL_TASK_COUNT,
+ new String[]{METRICS_THREAD_POOL_NAME}, new String[]{threadPoolName},
+ (double) info.getTotalTaskCount());
+ createOrUpdateGaugeSample(METRICS_THREAD_POOL_CORE_POOL_SIZE,
+ new String[]{METRICS_THREAD_POOL_NAME}, new String[]{threadPoolName},
+ info.getCorePoolSize());
+ createOrUpdateGaugeSample(METRICS_THREAD_POOL_MAXIMUM_POOL_SIZE,
+ new String[]{METRICS_THREAD_POOL_NAME}, new String[]{threadPoolName},
+ info.getMaximumPoolSize());
+ createOrUpdateGaugeSample(METRICS_THREAD_POOL_LARGEST_POOL_SIZE,
+ new String[]{METRICS_THREAD_POOL_NAME}, new String[]{threadPoolName},
+ info.getLargestPoolSize());
+ createOrUpdateGaugeSample(METRICS_THREAD_POOL_QUEUE_REMAINING_CAPACITY,
+ new String[]{METRICS_THREAD_POOL_NAME}, new String[]{threadPoolName},
+ info.getQueueRemainingCapacity());
+ }
+
+
+ @Override
+ public boolean isMetricsSampleUpdated() {
+ // memory status special
+ return true;
+ }
+
+ @Override
+ public Map getThreadPoolInfo() {
+ return executorMap;
+ }
+
+ @Override
+ public ApolloThreadPoolInfo getRemoteConfigRepositoryThreadPoolInfo() {
+ return executorMap.get(REMOTE_CONFIG_REPOSITORY);
+ }
+
+ @Override
+ public ApolloThreadPoolInfo getAbstractConfigThreadPoolInfo() {
+ return executorMap.get(ABSTRACT_CONFIG);
+ }
+
+ @Override
+ public ApolloThreadPoolInfo getAbstractConfigFileThreadPoolInfo() {
+ return executorMap.get(ABSTRACT_CONFIG_FILE);
+ }
+
+ @Override
+ public ApolloThreadPoolInfo getMetricsExporterThreadPoolInfo() {
+ return executorMap.get(METRICS_EXPORTER);
+ }
+}
\ No newline at end of file
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/NullClientBootstrapArgsMonitorApi.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/NullClientBootstrapArgsMonitorApi.java
new file mode 100644
index 00000000..88ddd7b8
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/NullClientBootstrapArgsMonitorApi.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.listener.impl;
+
+import com.ctrip.framework.apollo.monitor.api.ApolloClientBootstrapArgsMonitorApi;
+import com.ctrip.framework.apollo.monitor.internal.jmx.mbean.ApolloClientJmxBootstrapArgsMBean;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * @author Rawven
+ */
+public class NullClientBootstrapArgsMonitorApi implements ApolloClientBootstrapArgsMonitorApi,
+ ApolloClientJmxBootstrapArgsMBean {
+
+ @Override
+ public Map getBootstrapArgsString() {
+ return Collections.emptyMap();
+ }
+}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/NullClientExceptionMonitorApi.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/NullClientExceptionMonitorApi.java
new file mode 100644
index 00000000..5a21f802
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/NullClientExceptionMonitorApi.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.listener.impl;
+
+import com.ctrip.framework.apollo.monitor.api.ApolloClientExceptionMonitorApi;
+import com.ctrip.framework.apollo.monitor.internal.jmx.mbean.ApolloClientJmxExceptionMBean;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author Rawven
+ */
+public class NullClientExceptionMonitorApi implements ApolloClientExceptionMonitorApi,
+ ApolloClientJmxExceptionMBean {
+
+ @Override
+ public List getApolloConfigExceptionList() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public Integer getExceptionCountFromStartup() {
+ return 0;
+ }
+
+ @Override
+ public List getApolloConfigExceptionDetails() {
+ return Collections.emptyList();
+ }
+}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/NullClientNamespaceMonitorApi.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/NullClientNamespaceMonitorApi.java
new file mode 100644
index 00000000..70a9b2ae
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/NullClientNamespaceMonitorApi.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.listener.impl;
+
+import com.ctrip.framework.apollo.monitor.api.ApolloClientNamespaceMonitorApi;
+import com.ctrip.framework.apollo.monitor.internal.jmx.mbean.ApolloClientJmxNamespaceMBean;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Rawven
+ */
+public class NullClientNamespaceMonitorApi implements ApolloClientNamespaceMonitorApi,
+ ApolloClientJmxNamespaceMBean {
+
+ @Override
+ public Map getNamespaceMetrics() {
+ return Collections.emptyMap();
+ }
+
+
+ @Override
+ public List getNotFoundNamespaces() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public List getTimeoutNamespaces() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public Map getNamespaceMetricsString() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public Integer getNamespacePropertySize(String namespace) {
+ return 0;
+ }
+
+}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/NullClientThreadPoolMonitorApi.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/NullClientThreadPoolMonitorApi.java
new file mode 100644
index 00000000..03e5c1b5
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/NullClientThreadPoolMonitorApi.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.listener.impl;
+
+import com.ctrip.framework.apollo.monitor.api.ApolloClientThreadPoolMonitorApi;
+import com.ctrip.framework.apollo.monitor.internal.jmx.mbean.ApolloClientJmxThreadPoolMBean;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * @author Rawven
+ */
+public class NullClientThreadPoolMonitorApi implements ApolloClientThreadPoolMonitorApi,
+ ApolloClientJmxThreadPoolMBean {
+
+ private final ApolloThreadPoolInfo NULL_THREAD_POOL_INFO = new ApolloThreadPoolInfo();
+
+ @Override
+ public Map getThreadPoolInfo() {
+ return Collections.emptyMap();
+ }
+
+ @Override
+ public ApolloThreadPoolInfo getRemoteConfigRepositoryThreadPoolInfo() {
+ return NULL_THREAD_POOL_INFO;
+ }
+
+ @Override
+ public ApolloThreadPoolInfo getAbstractConfigThreadPoolInfo() {
+ return NULL_THREAD_POOL_INFO;
+ }
+
+ @Override
+ public ApolloThreadPoolInfo getAbstractConfigFileThreadPoolInfo() {
+ return NULL_THREAD_POOL_INFO;
+ }
+
+ @Override
+ public ApolloThreadPoolInfo getMetricsExporterThreadPoolInfo() {
+ return NULL_THREAD_POOL_INFO;
+ }
+}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/model/CounterModel.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/model/CounterModel.java
new file mode 100644
index 00000000..566ce718
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/model/CounterModel.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.model;
+
+import com.ctrip.framework.apollo.monitor.internal.enums.MetricTypeEnums;
+
+/**
+ * @author Rawven
+ */
+public class CounterModel extends SampleModel {
+
+ private CounterModel(String name, double num) {
+ if (name == null || name.isEmpty()) {
+ throw new IllegalArgumentException("Name cannot be null or empty");
+ }
+ if (Double.isNaN(num) || Double.isInfinite(num)) {
+ throw new IllegalArgumentException("Number must be a valid double");
+ }
+ setName(name);
+ setType(MetricTypeEnums.COUNTER);
+ this.value.set(num);
+ }
+
+ public static CounterModel create(String name, double value) {
+ return new CounterModel(name, value);
+ }
+
+ public void increase(double value) {
+ if (Double.isNaN(value) || Double.isInfinite(value)) {
+ throw new IllegalArgumentException("Value must be a valid double");
+ }
+ this.value.addAndGet(value);
+ }
+
+ public double getIncreaseValueAndResetZero() {
+ return value.getAndSet(0.0);
+ }
+
+}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/model/GaugeModel.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/model/GaugeModel.java
new file mode 100644
index 00000000..0abf0699
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/model/GaugeModel.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.model;
+
+import com.ctrip.framework.apollo.monitor.internal.enums.MetricTypeEnums;
+
+/**
+ * @author Rawven
+ */
+public class GaugeModel extends SampleModel {
+
+ private GaugeModel(String name, double value) {
+ if (name == null || name.isEmpty()) {
+ throw new IllegalArgumentException("Name cannot be null or empty");
+ }
+ setName(name);
+ setType(MetricTypeEnums.GAUGE);
+ this.value.set(value);
+ }
+
+ public static GaugeModel create(String name, double value) {
+ return new GaugeModel(name, value);
+ }
+
+}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/model/SampleModel.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/model/SampleModel.java
new file mode 100644
index 00000000..177d7bd3
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/model/SampleModel.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.model;
+
+import com.ctrip.framework.apollo.monitor.internal.enums.MetricTypeEnums;
+import com.google.common.util.concurrent.AtomicDouble;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Rawven
+ */
+public class SampleModel {
+
+ protected final AtomicDouble value = new AtomicDouble();
+ private final Map tags = new HashMap<>(1);
+ private String name;
+ private MetricTypeEnums type;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public SampleModel putTag(String key, String value) {
+ tags.put(key, value);
+ return this;
+ }
+
+ public SampleModel putTags(Map tags) {
+ this.tags.putAll(tags);
+ return this;
+ }
+
+ public MetricTypeEnums getType() {
+ return type;
+ }
+
+ public void setType(MetricTypeEnums type) {
+ this.type = type;
+ }
+
+ public Map getTags() {
+ return Collections.unmodifiableMap(tags);
+ }
+
+ public double getValue() {
+ return value.get();
+ }
+
+ public void setValue(double value) {
+ this.value.set(value);
+ }
+}
+
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/tracer/ApolloClientMessageProducerComposite.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/tracer/ApolloClientMessageProducerComposite.java
new file mode 100644
index 00000000..8ae27a72
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/tracer/ApolloClientMessageProducerComposite.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.tracer;
+
+import com.ctrip.framework.apollo.tracer.internals.NullTransaction;
+import com.ctrip.framework.apollo.tracer.spi.MessageProducer;
+import com.ctrip.framework.apollo.tracer.spi.Transaction;
+import java.util.List;
+
+/**
+ * message producer composite
+ *
+ * @author Rawven
+ */
+public class ApolloClientMessageProducerComposite implements MessageProducer {
+
+ public static final NullTransaction NULL_TRANSACTION = new NullTransaction();
+ private final List producers;
+
+ public ApolloClientMessageProducerComposite(List producers) {
+ this.producers = producers;
+ }
+
+ @Override
+ public void logError(Throwable cause) {
+ for (MessageProducer producer : producers) {
+ producer.logError(cause);
+ }
+ }
+
+ @Override
+ public void logError(String message, Throwable cause) {
+ for (MessageProducer producer : producers) {
+ producer.logError(message, cause);
+ }
+ }
+
+ @Override
+ public void logEvent(String type, String name) {
+ for (MessageProducer producer : producers) {
+ producer.logEvent(type, name);
+ }
+ }
+
+ @Override
+ public void logEvent(String type, String name, String status, String nameValuePairs) {
+ for (MessageProducer producer : producers) {
+ producer.logEvent(type, name, status, nameValuePairs);
+ }
+ }
+
+ @Override
+ public void logMetricsForCount(String name) {
+ for (MessageProducer producer : producers) {
+ producer.logMetricsForCount(name);
+ }
+ }
+
+ @Override
+ public Transaction newTransaction(String type, String name) {
+ for (MessageProducer producer : producers) {
+ Transaction transaction = producer.newTransaction(type, name);
+ if (transaction != null) {
+ return transaction;
+ }
+ }
+ return NULL_TRANSACTION;
+ }
+}
\ No newline at end of file
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/tracer/ApolloClientMessageProducerManager.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/tracer/ApolloClientMessageProducerManager.java
new file mode 100644
index 00000000..748f2c79
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/tracer/ApolloClientMessageProducerManager.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.tracer;
+
+import com.ctrip.framework.apollo.internals.ConfigMonitorInitializer;
+import com.ctrip.framework.apollo.tracer.spi.MessageProducer;
+import com.ctrip.framework.apollo.tracer.spi.MessageProducerManager;
+
+/**
+ * @author Rawven
+ */
+public class ApolloClientMessageProducerManager implements MessageProducerManager {
+
+ private static MessageProducer producer;
+
+ public ApolloClientMessageProducerManager() {
+ producer = ConfigMonitorInitializer.initializeMessageProducerComposite();
+ }
+
+ @Override
+ public MessageProducer getProducer() {
+ return producer;
+ }
+
+ @Override
+ public int getOrder() {
+ return -1;
+ }
+}
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/tracer/ApolloClientMonitorMessageProducer.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/tracer/ApolloClientMonitorMessageProducer.java
new file mode 100644
index 00000000..b744ce2d
--- /dev/null
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/monitor/internal/tracer/ApolloClientMonitorMessageProducer.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.tracer;
+
+import static com.ctrip.framework.apollo.monitor.internal.ApolloClientMonitorConstant.*;
+import static com.ctrip.framework.apollo.monitor.internal.tracer.ApolloClientMessageProducerComposite.NULL_TRANSACTION;
+
+
+import com.ctrip.framework.apollo.exceptions.ApolloConfigException;
+import com.ctrip.framework.apollo.monitor.internal.event.ApolloClientMonitorEventFactory;
+import com.ctrip.framework.apollo.monitor.internal.event.ApolloClientMonitorEventPublisher;
+import com.ctrip.framework.apollo.tracer.spi.MessageProducer;
+import com.ctrip.framework.apollo.tracer.spi.Transaction;
+
+import java.time.LocalDate;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author Rawven
+ */
+public class ApolloClientMonitorMessageProducer implements MessageProducer {
+
+ public static final List TAGS = Collections.unmodifiableList(Arrays.asList(
+ APOLLO_CLIENT_CONFIGCHANGES,
+ APOLLO_CONFIG_EXCEPTION,
+ APOLLO_META_SERVICE,
+ APOLLO_CONFIG_SERVICES,
+ APOLLO_CLIENT_VERSION,
+ APOLLO_CONFIGSERVICE,
+ APOLLO_CLIENT_CONFIGMETA,
+ APOLLO_CLIENT_NAMESPACE_TIMEOUT,
+ APOLLO_CLIENT_NAMESPACE_USAGE,
+ APOLLO_CLIENT_NAMESPACE_NOT_FOUND
+ ));
+
+ @Override
+ public void logError(Throwable cause) {
+ publishErrorEvent(cause);
+ }
+
+ @Override
+ public void logError(String message, Throwable cause) {
+ publishErrorEvent(cause);
+ }
+
+ @Override
+ public void logEvent(String type, String name) {
+ if (TAGS.contains(type)) {
+ handleTaggedEvent(type, name);
+ } else if (type.startsWith(APOLLO_CLIENT_CONFIGS)) {
+ handleClientConfigEvent(type, name);
+ } else if (type.startsWith(APOLLO_CLIENT_NAMESPACE_FIRST_LOAD_SPEND)) {
+ handleFirstLoadTimeEvent(type, name);
+ }
+ }
+
+ private void handleTaggedEvent(String type, String name) {
+ switch (type) {
+ case APOLLO_CONFIGSERVICE:
+ name = name.substring(APOLLO_CONFIGSERVICE_HELP_STR.length());
+ // fall through
+ case APOLLO_CLIENT_CONFIGCHANGES:
+ publishConfigChangeEvent(name);
+ break;
+ case APOLLO_CONFIG_EXCEPTION:
+ logError(new ApolloConfigException(name));
+ break;
+ case APOLLO_META_SERVICE:
+ publishMetaServiceEvent();
+ break;
+ case APOLLO_CONFIG_SERVICES:
+ publishConfigServiceEvent(name);
+ break;
+ case APOLLO_CLIENT_VERSION:
+ publishClientVersionEvent(name);
+ break;
+ case APOLLO_CLIENT_NAMESPACE_TIMEOUT:
+ publishNamespaceTimeoutEvent(name);
+ break;
+ case APOLLO_CLIENT_NAMESPACE_NOT_FOUND:
+ publishNamespaceNotFoundEvent(name);
+ break;
+ case APOLLO_CLIENT_CONFIGMETA:
+ // 不需要收集
+ break;
+ default:
+ break;
+ }
+ }
+
+
+ private void publishErrorEvent(Throwable cause) {
+ ApolloClientMonitorEventPublisher.publish(
+ ApolloClientMonitorEventFactory.getInstance().createEvent(TAG_ERROR)
+ .withTag(TAG_ERROR)
+ .putAttachment(THROWABLE, cause));
+ }
+
+ private void publishConfigChangeEvent(String name) {
+ ApolloClientMonitorEventPublisher.publish(
+ ApolloClientMonitorEventFactory.getInstance()
+ .createEvent(METRICS_NAMESPACE_LATEST_UPDATE_TIME)
+ .putAttachment(NAMESPACE, name)
+ .withTag(TAG_NAMESPACE));
+ }
+
+ private void publishMetaServiceEvent() {
+ ApolloClientMonitorEventPublisher.publish(
+ ApolloClientMonitorEventFactory.getInstance().createEvent(META_FRESH)
+ .withTag(TAG_BOOTSTRAP)
+ .putAttachment(META_FRESH, LocalDate.now().toString()));
+ }
+
+ private void publishConfigServiceEvent(String name) {
+ ApolloClientMonitorEventPublisher.publish(
+ ApolloClientMonitorEventFactory.getInstance().createEvent(CONFIG_SERVICE_URL)
+ .withTag(TAG_BOOTSTRAP)
+ .putAttachment(CONFIG_SERVICE_URL, name));
+ }
+
+ private void publishClientVersionEvent(String name) {
+ ApolloClientMonitorEventPublisher.publish(
+ ApolloClientMonitorEventFactory.getInstance().createEvent(VERSION)
+ .withTag(TAG_BOOTSTRAP)
+ .putAttachment(VERSION, name));
+ }
+
+ private void publishNamespaceTimeoutEvent(String name) {
+ ApolloClientMonitorEventPublisher.publish(
+ ApolloClientMonitorEventFactory.getInstance().createEvent(APOLLO_CLIENT_NAMESPACE_TIMEOUT)
+ .putAttachment(NAMESPACE, name)
+ .withTag(TAG_NAMESPACE));
+ }
+
+ private void publishNamespaceNotFoundEvent(String name) {
+ ApolloClientMonitorEventPublisher.publish(
+ ApolloClientMonitorEventFactory.getInstance().createEvent(APOLLO_CLIENT_NAMESPACE_NOT_FOUND)
+ .withTag(TAG_NAMESPACE)
+ .putAttachment(NAMESPACE, name));
+ }
+
+ private void handleClientConfigEvent(String type, String name) {
+ String namespace = type.substring(APOLLO_CLIENT_CONFIGS.length());
+ ApolloClientMonitorEventPublisher.publish(
+ ApolloClientMonitorEventFactory.getInstance().createEvent(NAMESPACE_RELEASE_KEY)
+ .withTag(TAG_NAMESPACE)
+ .putAttachment(NAMESPACE_RELEASE_KEY, name)
+ .putAttachment(NAMESPACE, namespace));
+ }
+
+ private void handleFirstLoadTimeEvent(String type, String name) {
+ String[] split = type.split(":");
+ String namespace = split[1];
+ long firstLoadTime = Long.parseLong(name);
+ ApolloClientMonitorEventPublisher.publish(
+ ApolloClientMonitorEventFactory.getInstance()
+ .createEvent(APOLLO_CLIENT_NAMESPACE_FIRST_LOAD_SPEND)
+ .putAttachment(NAMESPACE, namespace)
+ .putAttachment(TIMESTAMP, firstLoadTime)
+ .withTag(TAG_NAMESPACE));
+ }
+
+ @Override
+ public void logEvent(String type, String name, String status, String nameValuePairs) {
+ // ignore
+ }
+
+ @Override
+ public void logMetricsForCount(String name) {
+ String[] split = name.split(":");
+ if (split.length == 2 && APOLLO_CLIENT_NAMESPACE_USAGE.equals(split[0])) {
+ ApolloClientMonitorEventPublisher.publish(
+ ApolloClientMonitorEventFactory.getInstance()
+ .createEvent(APOLLO_CLIENT_NAMESPACE_USAGE)
+ .putAttachment(NAMESPACE, split[1])
+ .withTag(TAG_NAMESPACE));
+ }
+ }
+
+ @Override
+ public Transaction newTransaction(String type, String name) {
+ return NULL_TRANSACTION;
+ }
+}
\ No newline at end of file
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/boot/ApolloApplicationContextInitializer.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/boot/ApolloApplicationContextInitializer.java
index 17890055..05decef5 100644
--- a/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/boot/ApolloApplicationContextInitializer.java
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/spring/boot/ApolloApplicationContextInitializer.java
@@ -93,7 +93,12 @@ public class ApolloApplicationContextInitializer implements
ApolloClientSystemConsts.APOLLO_CONFIG_SERVICE,
ApolloClientSystemConsts.APOLLO_PROPERTY_ORDER_ENABLE,
ApolloClientSystemConsts.APOLLO_PROPERTY_NAMES_CACHE_ENABLE,
- ApolloClientSystemConsts.APOLLO_OVERRIDE_SYSTEM_PROPERTIES};
+ ApolloClientSystemConsts.APOLLO_OVERRIDE_SYSTEM_PROPERTIES,
+ ApolloClientSystemConsts.APOLLO_CLIENT_MONITOR_EXTERNAL_TYPE,
+ ApolloClientSystemConsts.APOLLO_CLIENT_MONITOR_ENABLED,
+ ApolloClientSystemConsts.APOLLO_CLIENT_MONITOR_EXTERNAL_EXPORT_PERIOD,
+ ApolloClientSystemConsts.APOLLO_CLIENT_MONITOR_JMX_ENABLED,
+ ApolloClientSystemConsts.APOLLO_CLIENT_MONITOR_EXCEPTION_QUEUE_SIZE,};
private final ConfigPropertySourceFactory configPropertySourceFactory = SpringInjector
.getInstance(ConfigPropertySourceFactory.class);
@@ -132,6 +137,7 @@ protected void initialize(ConfigurableEnvironment environment) {
}
String namespaces = environment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_NAMESPACES, ConfigConsts.NAMESPACE_APPLICATION);
+ System.setProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_NAMESPACES, namespaces);
logger.debug("Apollo bootstrap namespaces: {}", namespaces);
List namespaceList = NAMESPACE_SPLITTER.splitToList(namespaces);
@@ -197,14 +203,14 @@ public void postProcessEnvironment(ConfigurableEnvironment configurableEnvironme
initializeSystemProperty(configurableEnvironment);
Boolean eagerLoadEnabled = configurableEnvironment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_EAGER_LOAD_ENABLED, Boolean.class, false);
-
+ System.setProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_EAGER_LOAD_ENABLED, String.valueOf(eagerLoadEnabled));
//EnvironmentPostProcessor should not be triggered if you don't want Apollo Loading before Logging System Initialization
if (!eagerLoadEnabled) {
return;
}
Boolean bootstrapEnabled = configurableEnvironment.getProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED, Boolean.class, false);
-
+ System.setProperty(PropertySourcesConstants.APOLLO_BOOTSTRAP_ENABLED, String.valueOf(bootstrapEnabled));
if (bootstrapEnabled) {
DeferredLogger.enable();
initialize(configurableEnvironment);
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/util/ConfigUtil.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/util/ConfigUtil.java
index 9b99a4bf..83a324f0 100644
--- a/apollo-client/src/main/java/com/ctrip/framework/apollo/util/ConfigUtil.java
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/util/ConfigUtil.java
@@ -71,6 +71,11 @@ public class ConfigUtil {
private boolean propertyNamesCacheEnabled = false;
private boolean propertyFileCacheEnabled = true;
private boolean overrideSystemProperties = true;
+ private boolean clientMonitorEnabled = false;
+ private boolean clientMonitorJmxEnabled = false;
+ private String monitorExternalType = "NONE";
+ private long monitorExternalExportPeriod = 10;
+ private int monitorExceptionQueueSize = 25;
public ConfigUtil() {
warnLogRateLimiter = RateLimiter.create(0.017); // 1 warning log output per minute
@@ -86,6 +91,11 @@ public ConfigUtil() {
initPropertyNamesCacheEnabled();
initPropertyFileCacheEnabled();
initOverrideSystemProperties();
+ initClientMonitorEnabled();
+ initClientMonitorJmxEnabled();
+ initClientMonitorExternalType();
+ initClientMonitorExternalExportPeriod();
+ initClientMonitorExceptionQueueSize();
}
/**
@@ -490,7 +500,75 @@ private void initOverrideSystemProperties() {
ApolloClientSystemConsts.APOLLO_OVERRIDE_SYSTEM_PROPERTIES,
overrideSystemProperties);
}
+
+
+ private void initClientMonitorExternalType() {
+ monitorExternalType = System.getProperty(ApolloClientSystemConsts.APOLLO_CLIENT_MONITOR_EXTERNAL_TYPE);
+ if (Strings.isNullOrEmpty(monitorExternalType)) {
+ monitorExternalType = Foundation.app()
+ .getProperty(ApolloClientSystemConsts.APOLLO_CLIENT_MONITOR_EXTERNAL_TYPE, "NONE");
+ }
+ }
+
+ public String getMonitorExternalType() {
+ return monitorExternalType;
+ }
+
+ private void initClientMonitorExternalExportPeriod() {
+ Integer value = getCustomizedIntegerValue(ApolloClientSystemConsts.APOLLO_CLIENT_MONITOR_EXTERNAL_EXPORT_PERIOD);
+
+ if (value != null) {
+ if (value <= 0) {
+ logger.warn("Config for {} is invalid: {}, remain default value: 10",
+ ApolloClientSystemConsts.APOLLO_CLIENT_MONITOR_EXTERNAL_EXPORT_PERIOD, value);
+ } else {
+ monitorExternalExportPeriod = value;
+ }
+ }
+ }
+
+ public long getMonitorExternalExportPeriod() {
+ return monitorExternalExportPeriod;
+ }
+
+ private void initClientMonitorEnabled() {
+ clientMonitorEnabled = getPropertyBoolean(ApolloClientSystemConsts.APOLLO_CLIENT_MONITOR_ENABLED,
+ ApolloClientSystemConsts.APOLLO_CLIENT_MONITOR_ENABLED,
+ clientMonitorEnabled);
+ }
+
+ public boolean isClientMonitorEnabled() {
+ return clientMonitorEnabled;
+ }
+
+ private void initClientMonitorJmxEnabled() {
+ clientMonitorJmxEnabled = getPropertyBoolean(ApolloClientSystemConsts.APOLLO_CLIENT_MONITOR_JMX_ENABLED,
+ ApolloClientSystemConsts.APOLLO_CLIENT_MONITOR_JMX_ENABLED,
+ clientMonitorJmxEnabled);
+ }
+
+ public boolean isClientMonitorJmxEnabled() {
+ return clientMonitorJmxEnabled;
+ }
+
+ private void initClientMonitorExceptionQueueSize() {
+ Integer value = getCustomizedIntegerValue(ApolloClientSystemConsts.APOLLO_CLIENT_MONITOR_EXCEPTION_QUEUE_SIZE);
+
+ if (value != null) {
+ if (value <= 0) {
+ logger.warn("Config for {} is invalid: {}, remain default value: 25",
+ ApolloClientSystemConsts.APOLLO_CLIENT_MONITOR_EXCEPTION_QUEUE_SIZE, value);
+ } else {
+ monitorExceptionQueueSize = value;
+ }
+ }
+ }
+
+ public int getMonitorExceptionQueueSize() {
+ return monitorExceptionQueueSize;
+ }
+
private boolean getPropertyBoolean(String propertyName, String envName, boolean defaultVal) {
String enablePropertyNamesCache = System.getProperty(propertyName);
if (Strings.isNullOrEmpty(enablePropertyNamesCache)) {
@@ -509,4 +587,4 @@ private boolean getPropertyBoolean(String propertyName, String envName, boolean
}
return defaultVal;
}
-}
+}
\ No newline at end of file
diff --git a/apollo-client/src/main/java/com/ctrip/framework/apollo/util/ExceptionUtil.java b/apollo-client/src/main/java/com/ctrip/framework/apollo/util/ExceptionUtil.java
index 28df3f5f..875246a1 100644
--- a/apollo-client/src/main/java/com/ctrip/framework/apollo/util/ExceptionUtil.java
+++ b/apollo-client/src/main/java/com/ctrip/framework/apollo/util/ExceptionUtil.java
@@ -64,4 +64,4 @@ public static String getDetailMessage(Throwable ex) {
return builder.toString();
}
-}
+}
\ No newline at end of file
diff --git a/apollo-client/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/apollo-client/src/main/resources/META-INF/additional-spring-configuration-metadata.json
index a14b1d03..0bb9b644 100644
--- a/apollo-client/src/main/resources/META-INF/additional-spring-configuration-metadata.json
+++ b/apollo-client/src/main/resources/META-INF/additional-spring-configuration-metadata.json
@@ -63,6 +63,42 @@
"description": "apollo config service address. if it's configured apollo client will not refresh config services from remote meta service.",
"defaultValue": ""
},
+ {
+ "name": "apollo.client.monitor.enabled",
+ "type": "java.lang.Boolean",
+ "sourceType": "com.ctrip.framework.apollo.core.ApolloClientSystemConsts",
+ "description": "apollo client monitor enabled.",
+ "defaultValue": false
+ },
+ {
+ "name": "apollo.client.monitor.jmx.enabled",
+ "type": "java.lang.Boolean",
+ "sourceType": "com.ctrip.framework.apollo.core.ApolloClientSystemConsts",
+ "description": "apollo client monitor jmx enabled.",
+ "defaultValue": false
+ },
+ {
+ "name": "apollo.client.monitor.external.type",
+ "type": "java.lang.String",
+ "sourceType": "com.ctrip.framework.apollo.core.ApolloClientSystemConsts",
+ "description": "apollo client monitor external monitoring system type.",
+ "defaultValue": ""
+ },
+
+ {
+ "name": "apollo.client.monitor.external.export-period",
+ "type": "java.lang.String",
+ "sourceType": "com.ctrip.framework.apollo.core.ApolloClientSystemConsts",
+ "description": "apollo client monitor external metrics export period.",
+ "defaultValue": ""
+ },
+ {
+ "name": "apollo.client.monitor.exception-queue-size",
+ "type": "java.lang.String",
+ "sourceType": "com.ctrip.framework.apollo.core.ApolloClientSystemConsts",
+ "description": "apollo client monitor exception-queue-size.",
+ "defaultValue": ""
+ },
{
"name": "apollo.meta",
"type": "java.net.URI",
diff --git a/apollo-client/src/main/resources/META-INF/services/com.ctrip.framework.apollo.tracer.spi.MessageProducerManager b/apollo-client/src/main/resources/META-INF/services/com.ctrip.framework.apollo.tracer.spi.MessageProducerManager
new file mode 100644
index 00000000..7b56522b
--- /dev/null
+++ b/apollo-client/src/main/resources/META-INF/services/com.ctrip.framework.apollo.tracer.spi.MessageProducerManager
@@ -0,0 +1 @@
+com.ctrip.framework.apollo.monitor.internal.tracer.ApolloClientMessageProducerManager
\ No newline at end of file
diff --git a/apollo-client/src/test/java/com/ctrip/framework/apollo/internals/ConfigMonitorInitializerTest.java b/apollo-client/src/test/java/com/ctrip/framework/apollo/internals/ConfigMonitorInitializerTest.java
new file mode 100644
index 00000000..25be1005
--- /dev/null
+++ b/apollo-client/src/test/java/com/ctrip/framework/apollo/internals/ConfigMonitorInitializerTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.internals;
+
+import com.ctrip.framework.apollo.build.MockInjector;
+import com.ctrip.framework.apollo.monitor.internal.ApolloClientMonitorContext;
+import com.ctrip.framework.apollo.monitor.internal.exporter.ApolloClientMetricsExporterFactory;
+import com.ctrip.framework.apollo.util.ConfigUtil;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class ConfigMonitorInitializerTest {
+
+ @Mock
+ private ConfigUtil mockConfigUtil;
+ @Mock
+ private ApolloClientMonitorContext mockMonitorContext;
+ @Mock
+ private ApolloClientMetricsExporterFactory mockExporterFactory;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ MockInjector.setInstance(ConfigUtil.class, mockConfigUtil);
+ MockInjector.setInstance(ApolloClientMonitorContext.class, mockMonitorContext);
+ MockInjector.setInstance(ApolloClientMetricsExporterFactory.class, mockExporterFactory);
+ resetConfigMonitorInitializer();
+ }
+
+ @Test
+ public void testInitializeWhenMonitorEnabledAndNotInitialized() {
+ when(mockConfigUtil.isClientMonitorEnabled()).thenReturn(true);
+ ConfigMonitorInitializer.initialize();
+ assertTrue(ConfigMonitorInitializer.hasInitialized);
+ //ConfigMonitorInitializer.53line + DefaultApolloClientBootstrapArgsApi.64line
+ verify(mockConfigUtil, times(2)).isClientMonitorEnabled();
+ }
+
+ @Test
+ public void testInitializeWhenMonitorDisabled() {
+ when(mockConfigUtil.isClientMonitorEnabled()).thenReturn(false);
+ ConfigMonitorInitializer.initialize();
+ assertFalse(ConfigMonitorInitializer.hasInitialized);
+ }
+
+ @Test
+ public void testInitializeWhenAlreadyInitialized() {
+ when(mockConfigUtil.isClientMonitorEnabled()).thenReturn(true);
+ ConfigMonitorInitializer.hasInitialized = true;
+ ConfigMonitorInitializer.initialize();
+ verify(mockConfigUtil, times(1)).isClientMonitorEnabled();
+ }
+
+ @Test
+ public void testReset() {
+ ConfigMonitorInitializer.reset();
+ assertFalse(ConfigMonitorInitializer.hasInitialized);
+ }
+
+ private void resetConfigMonitorInitializer() {
+ ConfigMonitorInitializer.reset();
+ }
+
+}
diff --git a/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/ApolloClientMonitorContextTest.java b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/ApolloClientMonitorContextTest.java
new file mode 100644
index 00000000..00d9145a
--- /dev/null
+++ b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/ApolloClientMonitorContextTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal;
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import com.ctrip.framework.apollo.monitor.internal.exporter.ApolloClientMetricsExporter;
+import com.ctrip.framework.apollo.monitor.internal.exporter.impl.NullApolloClientMetricsExporter;
+import com.ctrip.framework.apollo.monitor.internal.listener.ApolloClientMonitorEventListener;
+import com.ctrip.framework.apollo.monitor.internal.listener.impl.DefaultApolloClientBootstrapArgsApi;
+import com.ctrip.framework.apollo.monitor.internal.listener.impl.DefaultApolloClientExceptionApi;
+import com.ctrip.framework.apollo.monitor.internal.listener.impl.DefaultApolloClientNamespaceApi;
+import com.ctrip.framework.apollo.monitor.internal.listener.impl.DefaultApolloClientThreadPoolApi;
+import com.ctrip.framework.apollo.monitor.internal.listener.impl.NullClientBootstrapArgsMonitorApi;
+import com.ctrip.framework.apollo.monitor.internal.listener.impl.NullClientExceptionMonitorApi;
+import com.ctrip.framework.apollo.monitor.internal.listener.impl.NullClientNamespaceMonitorApi;
+import com.ctrip.framework.apollo.monitor.internal.listener.impl.NullClientThreadPoolMonitorApi;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+public class ApolloClientMonitorContextTest {
+
+ @Mock
+ private DefaultApolloClientExceptionApi exceptionMonitorApi;
+ @Mock
+ private DefaultApolloClientNamespaceApi namespaceMonitorApi;
+ @Mock
+ private DefaultApolloClientBootstrapArgsApi bootstrapArgsMonitorApi;
+ @Mock
+ private DefaultApolloClientThreadPoolApi threadPoolMonitorApi;
+ @Mock
+ private ApolloClientMetricsExporter metricsExporter;
+
+ private ApolloClientMonitorContext monitorContext;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ monitorContext = new ApolloClientMonitorContext();
+ }
+
+ @Test
+ public void testInitContext(){
+ assertTrue(monitorContext.getBootstrapArgsApi() instanceof NullClientBootstrapArgsMonitorApi);
+ assertTrue(monitorContext.getNamespaceApi() instanceof NullClientNamespaceMonitorApi);
+ assertTrue(monitorContext.getThreadPoolApi() instanceof NullClientThreadPoolMonitorApi);
+ assertTrue(monitorContext.getExceptionApi() instanceof NullClientExceptionMonitorApi);
+ assertTrue(monitorContext.getMetricsExporter() instanceof NullApolloClientMetricsExporter);
+ }
+
+ @Test
+ public void testSettingAndGettingApis() {
+ monitorContext.setApolloClientExceptionMonitorApi(exceptionMonitorApi);
+ monitorContext.setApolloClientNamespaceMonitorApi(namespaceMonitorApi);
+ monitorContext.setApolloClientBootstrapArgsMonitorApi(bootstrapArgsMonitorApi);
+ monitorContext.setApolloClientThreadPoolMonitorApi(threadPoolMonitorApi);
+ monitorContext.setApolloClientMetricsExporter(metricsExporter);
+
+ assertSame(exceptionMonitorApi, monitorContext.getExceptionApi());
+ assertSame(namespaceMonitorApi, monitorContext.getNamespaceApi());
+ assertSame(bootstrapArgsMonitorApi, monitorContext.getBootstrapArgsApi());
+ assertSame(threadPoolMonitorApi, monitorContext.getThreadPoolApi());
+ assertSame(metricsExporter, monitorContext.getMetricsExporter());
+ }
+
+ @Test
+ public void testGetCollectors() {
+ ApolloClientMonitorEventListener listener = Mockito.mock(ApolloClientMonitorEventListener.class);
+ ApolloClientMonitorEventListener listener2 = Mockito.mock(ApolloClientMonitorEventListener.class);
+ monitorContext.addApolloClientMonitorEventListener(listener);
+ monitorContext.addApolloClientMonitorEventListener(listener2);
+
+ List listeners = monitorContext.getApolloClientMonitorEventListeners();
+ assertEquals(2, listeners.size());
+ }
+}
diff --git a/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/DefaultConfigMonitorTest.java b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/DefaultConfigMonitorTest.java
new file mode 100644
index 00000000..2f1ffc6c
--- /dev/null
+++ b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/DefaultConfigMonitorTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal;
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import com.ctrip.framework.apollo.build.ApolloInjector;
+import com.ctrip.framework.apollo.build.MockInjector;
+import com.ctrip.framework.apollo.monitor.api.ApolloClientBootstrapArgsMonitorApi;
+import com.ctrip.framework.apollo.monitor.api.ApolloClientExceptionMonitorApi;
+import com.ctrip.framework.apollo.monitor.api.ApolloClientNamespaceMonitorApi;
+import com.ctrip.framework.apollo.monitor.api.ApolloClientThreadPoolMonitorApi;
+import com.ctrip.framework.apollo.monitor.internal.exporter.ApolloClientMetricsExporter;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+public class DefaultConfigMonitorTest {
+
+ @Mock
+ private ApolloClientExceptionMonitorApi exceptionMonitorApi;
+ @Mock
+ private ApolloClientNamespaceMonitorApi namespaceMonitorApi;
+ @Mock
+ private ApolloClientBootstrapArgsMonitorApi bootstrapArgsMonitorApi;
+ @Mock
+ private ApolloClientThreadPoolMonitorApi threadPoolMonitorApi;
+ @Mock
+ private ApolloClientMetricsExporter metricsExporter;
+ @Mock
+ private ApolloClientMonitorContext monitorContext;
+
+ private DefaultConfigMonitor configMonitor;
+
+ @Before
+ public void setUp(){
+ MockitoAnnotations.initMocks(this);
+ when(monitorContext.getExceptionApi()).thenReturn(exceptionMonitorApi);
+ when(monitorContext.getNamespaceApi()).thenReturn(namespaceMonitorApi);
+ when(monitorContext.getBootstrapArgsApi()).thenReturn(bootstrapArgsMonitorApi);
+ when(monitorContext.getThreadPoolApi()).thenReturn(threadPoolMonitorApi);
+ when(monitorContext.getMetricsExporter()).thenReturn(metricsExporter);
+ MockInjector.setInstance(ApolloClientMonitorContext.class, monitorContext);
+
+ configMonitor = new DefaultConfigMonitor();
+
+ }
+
+ @Test
+ public void testApis(){
+ assertSame(exceptionMonitorApi, configMonitor.getExceptionMonitorApi());
+ assertSame(namespaceMonitorApi, configMonitor.getNamespaceMonitorApi());
+ assertSame(bootstrapArgsMonitorApi, configMonitor.getRunningParamsMonitorApi());
+ assertSame(threadPoolMonitorApi, configMonitor.getThreadPoolMonitorApi());
+ }
+
+ @Test
+ public void testExporterData(){
+ String data = "data";
+ when(metricsExporter.response()).thenReturn(data);
+
+ assertEquals(data, configMonitor.getExporterData());
+ }
+}
diff --git a/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/event/ApolloClientMonitorEventFactoryTest.java b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/event/ApolloClientMonitorEventFactoryTest.java
new file mode 100644
index 00000000..876ad176
--- /dev/null
+++ b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/event/ApolloClientMonitorEventFactoryTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.event;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class ApolloClientMonitorEventFactoryTest {
+
+ private ApolloClientMonitorEventFactory factory;
+
+ @Before
+ public void setUp() {
+ factory = ApolloClientMonitorEventFactory.getInstance();
+ }
+
+ @Test
+ public void testGetInstance() {
+ ApolloClientMonitorEventFactory instance1 = ApolloClientMonitorEventFactory.getInstance();
+ ApolloClientMonitorEventFactory instance2 = ApolloClientMonitorEventFactory.getInstance();
+
+ assertNotNull(instance1);
+ assertNotNull(instance2);
+ // 验证两个实例是同一个
+ assertSame(instance1, instance2);
+ }
+
+ @Test
+ public void testCreateEvent() {
+ String eventName = "TestEvent";
+ ApolloClientMonitorEvent event = factory.createEvent(eventName);
+
+ assertNotNull(event);
+ assertEquals(eventName, event.getName());
+ }
+}
diff --git a/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/event/ApolloClientMonitorEventPublisherTest.java b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/event/ApolloClientMonitorEventPublisherTest.java
new file mode 100644
index 00000000..04241217
--- /dev/null
+++ b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/event/ApolloClientMonitorEventPublisherTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.event;
+
+import static org.mockito.Mockito.*;
+
+import com.ctrip.framework.apollo.build.MockInjector;
+import com.ctrip.framework.apollo.monitor.internal.listener.ApolloClientMonitorEventListener;
+import com.ctrip.framework.apollo.monitor.internal.ApolloClientMonitorContext;
+import com.ctrip.framework.apollo.util.ConfigUtil;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Collections;
+
+public class ApolloClientMonitorEventPublisherTest {
+
+ private ApolloClientMonitorContext mockCollectorManager;
+ private ConfigUtil mockConfigUtil;
+ private ApolloClientMonitorEventListener mockListener;
+ private ApolloClientMonitorEvent mockEvent;
+
+ @Before
+ public void setUp() {
+ mockCollectorManager = mock(ApolloClientMonitorContext.class);
+ mockConfigUtil = mock(ConfigUtil.class);
+ mockListener = mock(ApolloClientMonitorEventListener.class);
+ mockEvent = mock(ApolloClientMonitorEvent.class);
+
+ // 使用 Mockito 来模拟静态方法
+ MockInjector.setInstance(ApolloClientMonitorContext.class, mockCollectorManager);
+ MockInjector.setInstance(ConfigUtil.class, mockConfigUtil);
+ ApolloClientMonitorEventPublisher.reset();
+ }
+
+ @Test
+ public void testPublish_WhenClientMonitorEnabled_CollectorSupportsEvent() {
+ when(mockConfigUtil.isClientMonitorEnabled()).thenReturn(true);
+ when(mockCollectorManager.getApolloClientMonitorEventListeners()).thenReturn(Collections.singletonList(
+ mockListener));
+ when(mockListener.isSupported(mockEvent)).thenReturn(true);
+
+ ApolloClientMonitorEventPublisher.publish(mockEvent);
+
+ verify(mockListener).collect(mockEvent);
+ }
+
+ @Test
+ public void testPublish_WhenClientMonitorEnabled_CollectorDoesNotSupportEvent() {
+ when(mockConfigUtil.isClientMonitorEnabled()).thenReturn(true);
+ when(mockCollectorManager.getApolloClientMonitorEventListeners()).thenReturn(Collections.singletonList(mockListener));
+ when(mockListener.isSupported(mockEvent)).thenReturn(false);
+
+ ApolloClientMonitorEventPublisher.publish(mockEvent);
+
+ verify(mockListener, never()).collect(mockEvent);
+ }
+
+ @Test
+ public void testPublish_WhenClientMonitorDisabled() {
+ when(mockConfigUtil.isClientMonitorEnabled()).thenReturn(false);
+
+ ApolloClientMonitorEventPublisher.publish(mockEvent);
+
+ verify(mockCollectorManager, never()).getApolloClientMonitorEventListeners();
+ }
+}
diff --git a/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/exporter/AbstractApolloClientMetricsExporterTest.java b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/exporter/AbstractApolloClientMetricsExporterTest.java
new file mode 100644
index 00000000..8c1454f8
--- /dev/null
+++ b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/exporter/AbstractApolloClientMetricsExporterTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.exporter;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import com.ctrip.framework.apollo.monitor.internal.enums.MetricTypeEnums;
+import com.ctrip.framework.apollo.monitor.internal.listener.ApolloClientMonitorEventListener;
+import com.ctrip.framework.apollo.monitor.internal.model.CounterModel;
+import com.ctrip.framework.apollo.monitor.internal.model.GaugeModel;
+import com.ctrip.framework.apollo.monitor.internal.model.SampleModel;
+import java.util.Collections;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AbstractApolloClientMetricsExporterTest {
+
+ private TestMetricsExporter exporter;
+ private ApolloClientMonitorEventListener mockListener;
+
+ @Before
+ public void setUp() {
+ exporter = new TestMetricsExporter();
+ mockListener = mock(ApolloClientMonitorEventListener.class);
+ }
+
+ @Test
+ public void testInit() {
+ List collectors = new ArrayList<>();
+ collectors.add(mockListener);
+ long collectPeriod = 10L;
+
+ exporter.init(collectors, collectPeriod);
+
+ assertEquals(collectors, exporter.getCollectors());
+ }
+
+ @Test
+ public void testUpdateMetricsData() {
+ List samples = new ArrayList<>();
+ GaugeModel gauge = mock(GaugeModel.class);
+ when(gauge.getType()).thenReturn(MetricTypeEnums.GAUGE);
+ when(gauge.getName()).thenReturn("testGauge");
+ when(gauge.getValue()).thenReturn(10.0);
+ samples.add(gauge);
+
+ when(mockListener.isMetricsSampleUpdated()).thenReturn(true);
+ when(mockListener.export()).thenReturn(samples);
+
+ exporter.init(Collections.singletonList(mockListener), 10L);
+ exporter.updateMetricsData();
+
+ verify(mockListener).export();
+ verify(gauge).getValue();
+ }
+
+ @Test
+ public void testRegisterSampleGauge() {
+ GaugeModel gaugeModel = (GaugeModel) GaugeModel.create("testGauge", 5.0).putTag("key", "value");
+
+ exporter.registerSample(gaugeModel);
+ }
+
+ @Test
+ public void testRegisterSampleCounter() {
+ CounterModel counterModel = (CounterModel) CounterModel.create("testCounter", 5.0)
+ .putTag("key", "value");
+ exporter.registerSample(counterModel);
+ }
+
+ private class TestMetricsExporter extends AbstractApolloClientMetricsExporter {
+
+ @Override
+ protected void doInit() {
+ }
+
+ public List getCollectors() {
+ return listeners;
+ }
+
+ @Override
+ public boolean isSupport(String form) {
+ return false;
+ }
+
+ @Override
+ public void registerOrUpdateCounterSample(String name, Map tag,
+ double incrValue) {
+
+ }
+
+ @Override
+ public void registerOrUpdateGaugeSample(String name, Map tag, double value) {
+
+ }
+
+ @Override
+ public String response() {
+ return "";
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/exporter/DefaultApolloClientMetricsExporterFactoryTest.java b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/exporter/DefaultApolloClientMetricsExporterFactoryTest.java
new file mode 100644
index 00000000..bce2079d
--- /dev/null
+++ b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/exporter/DefaultApolloClientMetricsExporterFactoryTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.exporter;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import com.ctrip.framework.apollo.build.MockInjector;
+import com.ctrip.framework.apollo.monitor.internal.exporter.impl.DefaultApolloClientMetricsExporterFactory;
+import com.ctrip.framework.apollo.monitor.internal.listener.ApolloClientMonitorEventListener;
+import com.ctrip.framework.apollo.util.ConfigUtil;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Collections;
+import java.util.List;
+
+public class DefaultApolloClientMetricsExporterFactoryTest {
+
+ private DefaultApolloClientMetricsExporterFactory factory;
+
+ @Mock
+ private ConfigUtil configUtil;
+
+ @Mock
+ private ApolloClientMonitorEventListener monitorEventListener;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ MockInjector.setInstance(ConfigUtil.class, configUtil);
+ factory = new DefaultApolloClientMetricsExporterFactory();
+ }
+
+ @Test
+ public void testGetMetricsReporter_NoExternalSystemType() {
+ when(configUtil.getMonitorExternalType()).thenReturn(null);
+
+ ApolloClientMetricsExporter result = factory.getMetricsReporter(Collections.emptyList());
+
+ assertNull(result);
+ verify(configUtil).getMonitorExternalType();
+ }
+
+ @Test
+ public void testGetMetricsReporter_ExporterFound() {
+ when(configUtil.getMonitorExternalType()).thenReturn("mocktheus");
+ when(configUtil.isClientMonitorJmxEnabled()).thenReturn(true);
+ when(configUtil.getMonitorExternalExportPeriod()).thenReturn(1000L);
+ when(monitorEventListener.getName()).thenReturn("testMBean");
+ List collectors = Collections.singletonList(monitorEventListener);
+
+ ApolloClientMetricsExporter result = factory.getMetricsReporter(collectors);
+
+ assertNotNull(result);
+ assertTrue(result instanceof MockApolloClientMetricsExporter);
+ }
+
+ @Test
+ public void testGetMetricsReporter_ExporterNotFound() {
+ when(configUtil.getMonitorExternalType()).thenReturn("unknownType");
+
+ ApolloClientMetricsExporter result = factory.getMetricsReporter(Collections.emptyList());
+
+ assertNull(result);
+ verify(configUtil).getMonitorExternalType();
+ }
+}
\ No newline at end of file
diff --git a/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/exporter/MockApolloClientMetricsExporter.java b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/exporter/MockApolloClientMetricsExporter.java
new file mode 100644
index 00000000..894af2dc
--- /dev/null
+++ b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/exporter/MockApolloClientMetricsExporter.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.exporter;
+
+import java.util.Map;
+
+public class MockApolloClientMetricsExporter extends AbstractApolloClientMetricsExporter {
+
+ @Override
+ protected void doInit() {
+
+ }
+
+ @Override
+ public boolean isSupport(String form) {
+ return "mocktheus".equals(form);
+ }
+
+ @Override
+ public void registerOrUpdateCounterSample(String name, Map tag,
+ double incrValue) {
+
+ }
+
+ @Override
+ public void registerOrUpdateGaugeSample(String name, Map tag, double value) {
+
+ }
+
+ @Override
+ public String response() {
+ return "";
+ }
+}
diff --git a/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/jmx/ApolloClientJmxMBeanRegisterTest.java b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/jmx/ApolloClientJmxMBeanRegisterTest.java
new file mode 100644
index 00000000..a3fec5de
--- /dev/null
+++ b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/jmx/ApolloClientJmxMBeanRegisterTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.jmx;
+
+import static org.mockito.Mockito.*;
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+public class ApolloClientJmxMBeanRegisterTest {
+
+ private MBeanServer mockMBeanServer;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mockMBeanServer = mock(MBeanServer.class);
+ ApolloClientJmxMBeanRegister.setMBeanServer(mockMBeanServer);
+ }
+
+ @Test
+ public void testRegister_MBeanNotRegistered() throws Exception {
+ String name = "com.example:type=TestMBean";
+ Object mbean = new Object();
+
+ when(mockMBeanServer.isRegistered(any(ObjectName.class))).thenReturn(false);
+ ObjectName objectName = ApolloClientJmxMBeanRegister.register(name, mbean);
+
+ assertNotNull(objectName);
+ verify(mockMBeanServer).registerMBean(mbean, objectName);
+ }
+
+ @Test
+ public void testRegister_MBeanAlreadyRegistered() throws Exception {
+ String name = "com.example:type=TestMBean";
+ Object mbean = new Object();
+
+ ObjectName objectName = new ObjectName(name);
+ when(mockMBeanServer.isRegistered(objectName)).thenReturn(true);
+
+ ApolloClientJmxMBeanRegister.register(name, mbean);
+
+ verify(mockMBeanServer).unregisterMBean(objectName);
+ verify(mockMBeanServer).registerMBean(mbean, objectName);
+ }
+
+}
diff --git a/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/DefaultApolloClientBootstrapArgsApiTest.java b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/DefaultApolloClientBootstrapArgsApiTest.java
new file mode 100644
index 00000000..c37699e1
--- /dev/null
+++ b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/DefaultApolloClientBootstrapArgsApiTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.listener.impl;
+
+import static com.ctrip.framework.apollo.core.ApolloClientSystemConsts.*;
+import static org.mockito.Mockito.*;
+import static org.junit.Assert.*;
+
+import com.ctrip.framework.apollo.core.enums.Env;
+import com.ctrip.framework.apollo.util.ConfigUtil;
+import com.ctrip.framework.apollo.monitor.internal.event.ApolloClientMonitorEvent;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Map;
+
+public class DefaultApolloClientBootstrapArgsApiTest {
+
+ private ConfigUtil configUtil;
+ private DefaultApolloClientBootstrapArgsApi api;
+
+ @Before
+ public void setUp() {
+ configUtil = mock(ConfigUtil.class);
+ when(configUtil.getAccessKeySecret()).thenReturn("secret");
+ when(configUtil.isAutoUpdateInjectedSpringPropertiesEnabled()).thenReturn(true);
+ when(configUtil.isOverrideSystemProperties()).thenReturn(false);
+ when(configUtil.getDefaultLocalCacheDir()).thenReturn("/cache");
+ when(configUtil.getCluster()).thenReturn("default");
+ when(configUtil.getAppId()).thenReturn("myApp");
+ when(configUtil.getApolloEnv()).thenReturn(Env.DEV);
+ when(configUtil.getMetaServerDomainName()).thenReturn("http://meta.server");
+
+ api = new DefaultApolloClientBootstrapArgsApi(configUtil);
+ }
+
+ @Test
+ public void testGetAccessKeySecret() {
+ assertEquals("secret", api.getAccessKeySecret());
+ }
+
+ @Test
+ public void testGetAutoUpdateInjectedSpringProperties() {
+ assertTrue(api.getAutoUpdateInjectedSpringProperties());
+ }
+
+ @Test
+ public void testGetCacheDir() {
+ assertEquals("/cache", api.getCacheDir());
+ }
+
+ @Test
+ public void testCollect0() {
+ ApolloClientMonitorEvent event = mock(ApolloClientMonitorEvent.class);
+ when(event.getName()).thenReturn(APOLLO_ACCESS_KEY_SECRET);
+ when(event.getAttachmentValue(APOLLO_ACCESS_KEY_SECRET)).thenReturn("newSecret");
+
+ api.collect0(event);
+
+ assertEquals("newSecret", api.getAccessKeySecret());
+ }
+
+ @Test
+ public void testUnhandledEvent() {
+ ApolloClientMonitorEvent event = mock(ApolloClientMonitorEvent.class);
+ when(event.getName()).thenReturn("unknownEvent");
+ api.collect0(event);
+ }
+
+ @Test
+ public void testGetBootstrapArgs() {
+ Map bootstrapArgs = api.getBootstrapArgs();
+ assertNotNull(bootstrapArgs);
+ assertTrue(bootstrapArgs.containsKey(APOLLO_ACCESS_KEY_SECRET));
+ }
+}
\ No newline at end of file
diff --git a/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/DefaultApolloClientExceptionApiTest.java b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/DefaultApolloClientExceptionApiTest.java
new file mode 100644
index 00000000..1dc115c7
--- /dev/null
+++ b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/DefaultApolloClientExceptionApiTest.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.listener.impl;
+
+import static com.ctrip.framework.apollo.monitor.internal.ApolloClientMonitorConstant.THROWABLE;
+import static org.mockito.Mockito.*;
+import static org.junit.Assert.*;
+
+import com.ctrip.framework.apollo.core.ApolloClientSystemConsts;
+import com.ctrip.framework.apollo.monitor.internal.event.ApolloClientMonitorEvent;
+import com.ctrip.framework.apollo.exceptions.ApolloConfigException;
+import com.ctrip.framework.apollo.util.ConfigUtil;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.List;
+
+public class DefaultApolloClientExceptionApiTest {
+
+ private DefaultApolloClientExceptionApi exceptionApi;
+
+ @Before
+ public void setUp() {
+ int someQueueSize = 10;
+ ConfigUtil configUtil = mock(ConfigUtil.class);
+ when(configUtil.getMonitorExceptionQueueSize()).thenReturn(someQueueSize);
+ System.setProperty(ApolloClientSystemConsts.APOLLO_CLIENT_MONITOR_EXCEPTION_QUEUE_SIZE, String.valueOf(someQueueSize));
+ exceptionApi = new DefaultApolloClientExceptionApi(configUtil);
+ }
+
+ @Test
+ public void testCollect0_AddsException() {
+ ApolloConfigException exception = new ApolloConfigException("Test Exception");
+ ApolloClientMonitorEvent event = mock(ApolloClientMonitorEvent.class);
+ when(event.getAttachmentValue(THROWABLE)).thenReturn(exception);
+
+ exceptionApi.collect0(event);
+
+ List exceptions = exceptionApi.getApolloConfigExceptionList();
+ assertEquals(1, exceptions.size());
+ assertEquals(exception, exceptions.get(0));
+ }
+
+ @Test
+ public void testCollect0_IncrementsExceptionCount() {
+ ApolloConfigException exception = new ApolloConfigException("Test Exception");
+ ApolloClientMonitorEvent event = mock(ApolloClientMonitorEvent.class);
+ when(event.getAttachmentValue(THROWABLE)).thenReturn(exception);
+
+ exceptionApi.collect0(event);
+ exceptionApi.collect0(event);
+
+ assertEquals(2, exceptionApi.getApolloConfigExceptionList().size());
+ }
+
+ @Test
+ public void testGetApolloConfigExceptionDetails() {
+ ApolloConfigException exception1 = new ApolloConfigException("First Exception");
+ ApolloConfigException exception2 = new ApolloConfigException("Second Exception");
+
+ ApolloClientMonitorEvent event1 = mock(ApolloClientMonitorEvent.class);
+ ApolloClientMonitorEvent event2 = mock(ApolloClientMonitorEvent.class);
+
+ when(event1.getAttachmentValue(THROWABLE)).thenReturn(exception1);
+ when(event2.getAttachmentValue(THROWABLE)).thenReturn(exception2);
+
+ exceptionApi.collect0(event1);
+ exceptionApi.collect0(event2);
+
+ List details = exceptionApi.getApolloConfigExceptionDetails();
+ assertEquals(2, details.size());
+ assertTrue(details.contains("First Exception"));
+ assertTrue(details.contains("Second Exception"));
+ }
+
+ @Test
+ public void testCollect0_HandlesMaxQueueSize() {
+ for (int i = 0; i < 10; i++) {
+ ApolloClientMonitorEvent event = mock(ApolloClientMonitorEvent.class);
+ when(event.getAttachmentValue(THROWABLE)).thenReturn(
+ new ApolloConfigException("Exception " + i));
+ exceptionApi.collect0(event);
+ }
+
+ assertEquals(10, exceptionApi.getApolloConfigExceptionList().size());
+
+ // Add one more to exceed the size.
+ ApolloClientMonitorEvent overflowEvent = mock(ApolloClientMonitorEvent.class);
+ when(overflowEvent.getAttachmentValue(THROWABLE)).thenReturn(
+ new ApolloConfigException("Overflow Exception"));
+ exceptionApi.collect0(overflowEvent);
+
+ assertEquals(10, exceptionApi.getApolloConfigExceptionList().size());
+ }
+}
\ No newline at end of file
diff --git a/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/DefaultApolloClientNamespaceApiTest.java b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/DefaultApolloClientNamespaceApiTest.java
new file mode 100644
index 00000000..e94b097f
--- /dev/null
+++ b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/DefaultApolloClientNamespaceApiTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.listener.impl;
+
+import com.ctrip.framework.apollo.Config;
+import com.ctrip.framework.apollo.internals.ConfigManager;
+import com.ctrip.framework.apollo.monitor.internal.event.ApolloClientMonitorEvent;
+import com.ctrip.framework.apollo.monitor.internal.event.ApolloClientMonitorEventFactory;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Collections;
+
+import static com.ctrip.framework.apollo.monitor.internal.ApolloClientMonitorConstant.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+public class DefaultApolloClientNamespaceApiTest {
+
+ @Mock
+ private ConfigManager configManager;
+
+ @Mock
+ private Config config;
+
+ @InjectMocks
+ private DefaultApolloClientNamespaceApi namespaceApi;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(configManager.getConfig(anyString())).thenReturn(config);
+ }
+
+ @Test
+ public void testCollectNamespaceNotFound() {
+ ApolloClientMonitorEvent event = ApolloClientMonitorEventFactory
+ .getInstance().createEvent(APOLLO_CLIENT_NAMESPACE_NOT_FOUND)
+ .putAttachment(NAMESPACE, "testNamespace");
+
+ namespaceApi.collect0(event);
+
+ assertTrue(namespaceApi.getNotFoundNamespaces().contains("testNamespace"));
+ }
+
+ @Test
+ public void testCollectNamespaceTimeout() {
+ ApolloClientMonitorEvent event = ApolloClientMonitorEventFactory
+ .getInstance().createEvent(APOLLO_CLIENT_NAMESPACE_TIMEOUT)
+ .putAttachment(NAMESPACE, "testNamespace");
+
+ namespaceApi.collect0(event);
+
+ assertTrue(namespaceApi.getTimeoutNamespaces().contains("testNamespace"));
+ }
+
+ @Test
+ public void testCollectNormalNamespace() {
+ ApolloClientMonitorEvent event = ApolloClientMonitorEventFactory
+ .getInstance().createEvent(APOLLO_CLIENT_NAMESPACE_USAGE)
+ .putAttachment(NAMESPACE, "testNamespace");
+
+ namespaceApi.collect0(event);
+
+ // Verify that the usage count has been incremented
+ assertEquals(1, namespaceApi.getNamespaceMetrics().get("testNamespace").getUsageCount());
+ }
+
+ @Test
+ public void testGetNamespacePropertySize() {
+ when(config.getPropertyNames()).thenReturn(Collections.singleton("property1"));
+
+ Integer propertySize = namespaceApi.getNamespacePropertySize("testNamespace");
+
+ assertEquals(Integer.valueOf(1), propertySize);
+ }
+
+ @Test
+ public void testExportMetrics() {
+ // Set up some initial state
+ ApolloClientMonitorEvent event = ApolloClientMonitorEventFactory
+ .getInstance().createEvent(APOLLO_CLIENT_NAMESPACE_USAGE)
+ .putAttachment(NAMESPACE, "testNamespace");
+ namespaceApi.collect0(event);
+
+ // Call the export method
+ namespaceApi.export0();
+
+ // Verify interactions with the configManager
+ verify(configManager).getConfig("testNamespace");
+ }
+}
\ No newline at end of file
diff --git a/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/DefaultApolloClientThreadPoolApiTest.java b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/DefaultApolloClientThreadPoolApiTest.java
new file mode 100644
index 00000000..f1d93b23
--- /dev/null
+++ b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/DefaultApolloClientThreadPoolApiTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.listener.impl;
+
+import static org.junit.Assert.*;
+
+import com.ctrip.framework.apollo.monitor.api.ApolloClientThreadPoolMonitorApi.ApolloThreadPoolInfo;
+import lombok.SneakyThrows;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadPoolExecutor;
+
+public class DefaultApolloClientThreadPoolApiTest {
+
+ private DefaultApolloClientThreadPoolApi threadPoolApi;
+ private ThreadPoolExecutor remoteConfigExecutor;
+ private ThreadPoolExecutor abstractConfigExecutor;
+ private ThreadPoolExecutor abstractConfigFileExecutor;
+ private ThreadPoolExecutor metricsExporterExecutor;
+
+ @Before
+ public void setUp() {
+ remoteConfigExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);
+ abstractConfigExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);
+ abstractConfigFileExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);
+ metricsExporterExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);
+ threadPoolApi = new DefaultApolloClientThreadPoolApi(
+ remoteConfigExecutor,
+ abstractConfigExecutor,
+ abstractConfigFileExecutor,
+ metricsExporterExecutor
+ );
+ }
+
+ @SneakyThrows
+ @Test
+ public void testExportThreadPoolMetrics() {
+ remoteConfigExecutor.execute(() -> {
+ });
+ remoteConfigExecutor.execute(() -> {
+ });
+ // 等待任务执行完成
+ Thread.sleep(200);
+ threadPoolApi.export0();
+
+ ApolloThreadPoolInfo info = threadPoolApi.getRemoteConfigRepositoryThreadPoolInfo();
+ assertEquals(0, info.getQueueSize());
+ assertEquals(2, info.getCompletedTaskCount());
+ assertEquals(2, info.getPoolSize());
+ }
+
+ @Test
+ public void testGetThreadPoolInfo() {
+ assertNotNull(threadPoolApi.getThreadPoolInfo());
+ assertEquals(4, threadPoolApi.getThreadPoolInfo().size());
+ }
+
+ @Test
+ public void testMetricsSampleUpdated() {
+ assertTrue(threadPoolApi.isMetricsSampleUpdated());
+ }
+
+ @Test
+ public void testGetAbstractConfigThreadPoolInfo() {
+ ApolloThreadPoolInfo info = threadPoolApi.getAbstractConfigThreadPoolInfo();
+ assertNotNull(info);
+ }
+
+ @Test
+ public void testGetAbstractConfigFileThreadPoolInfo() {
+ ApolloThreadPoolInfo info = threadPoolApi.getAbstractConfigFileThreadPoolInfo();
+ assertNotNull(info);
+ }
+
+
+}
diff --git a/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/NullClientBootstrapArgsMonitorApiTest.java b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/NullClientBootstrapArgsMonitorApiTest.java
new file mode 100644
index 00000000..d8ece6a1
--- /dev/null
+++ b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/NullClientBootstrapArgsMonitorApiTest.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.listener.impl;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Map;
+
+public class NullClientBootstrapArgsMonitorApiTest {
+
+ private NullClientBootstrapArgsMonitorApi bootstrapArgsMonitorApi;
+
+ @Before
+ public void setUp() {
+ bootstrapArgsMonitorApi = new NullClientBootstrapArgsMonitorApi();
+ }
+
+ @Test
+ public void testGetStartupParams() {
+ assertEquals(null, bootstrapArgsMonitorApi.getStartupArg("testKey"));
+ }
+
+ @Test
+ public void testGetConfigServiceUrl() {
+ assertEquals("", bootstrapArgsMonitorApi.getConfigServiceUrl());
+ }
+
+ @Test
+ public void testGetAccessKeySecret() {
+ assertEquals("", bootstrapArgsMonitorApi.getAccessKeySecret());
+ }
+
+ @Test
+ public void testGetAutoUpdateInjectedSpringProperties() {
+ assertFalse(bootstrapArgsMonitorApi.getAutoUpdateInjectedSpringProperties());
+ }
+
+ @Test
+ public void testIsBootstrapEnabled() {
+ assertFalse(bootstrapArgsMonitorApi.isBootstrapEnabled());
+ }
+
+ @Test
+ public void testGetBootstrapNamespaces() {
+ assertEquals("", bootstrapArgsMonitorApi.getBootstrapNamespaces());
+ }
+
+ @Test
+ public void testIsBootstrapEagerLoadEnabled() {
+ assertFalse(bootstrapArgsMonitorApi.isBootstrapEagerLoadEnabled());
+ }
+
+ @Test
+ public void testIsOverrideSystemProperties() {
+ assertFalse(bootstrapArgsMonitorApi.isOverrideSystemProperties());
+ }
+
+ @Test
+ public void testGetCacheDir() {
+ assertEquals("", bootstrapArgsMonitorApi.getCacheDir());
+ }
+
+ @Test
+ public void testGetCluster() {
+ assertEquals("", bootstrapArgsMonitorApi.getCluster());
+ }
+
+ @Test
+ public void testGetConfigService() {
+ assertEquals("", bootstrapArgsMonitorApi.getConfigService());
+ }
+
+ @Test
+ public void testIsClientMonitorEnabled() {
+ assertFalse(bootstrapArgsMonitorApi.isClientMonitorEnabled());
+ }
+
+ @Test
+ public void testIsClientMonitorJmxEnabled() {
+ assertFalse(bootstrapArgsMonitorApi.isClientMonitorJmxEnabled());
+ }
+
+ @Test
+ public void testGetClientMonitorExternalForm() {
+ assertEquals("", bootstrapArgsMonitorApi.getClientMonitorExternalForm());
+ }
+
+ @Test
+ public void testGetClientMonitorExternalExportPeriod() {
+ assertEquals(0, bootstrapArgsMonitorApi.getClientMonitorExternalExportPeriod());
+ }
+
+ @Test
+ public void testGetClientMonitorExceptionSaveSize() {
+ assertEquals(0, bootstrapArgsMonitorApi.getClientMonitorExceptionSaveSize());
+ }
+
+ @Test
+ public void testGetApolloMeta() {
+ assertEquals("", bootstrapArgsMonitorApi.getApolloMeta());
+ }
+
+ @Test
+ public void testGetMetaLatestFreshTime() {
+ assertEquals("", bootstrapArgsMonitorApi.getMetaLatestFreshTime());
+ }
+
+ @Test
+ public void testIsPropertyNamesCacheEnable() {
+ assertFalse(bootstrapArgsMonitorApi.isPropertyNamesCacheEnable());
+ }
+
+ @Test
+ public void testIsPropertyOrderEnable() {
+ assertFalse(bootstrapArgsMonitorApi.isPropertyOrderEnable());
+ }
+
+ @Test
+ public void testGetVersion() {
+ assertEquals("", bootstrapArgsMonitorApi.getVersion());
+ }
+
+ @Test
+ public void testGetEnv() {
+ assertEquals("", bootstrapArgsMonitorApi.getEnv());
+ }
+
+ @Test
+ public void testGetAppId() {
+ assertEquals("", bootstrapArgsMonitorApi.getAppId());
+ }
+
+ @Test
+ public void testGetBootstrapArgs() {
+ Map bootstrapArgs = bootstrapArgsMonitorApi.getBootstrapArgs();
+
+ assertNotNull(bootstrapArgs);
+ assertTrue(bootstrapArgs.isEmpty());
+ }
+}
diff --git a/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/NullClientExceptionMonitorApiTest.java b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/NullClientExceptionMonitorApiTest.java
new file mode 100644
index 00000000..1412358d
--- /dev/null
+++ b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/NullClientExceptionMonitorApiTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.listener.impl;
+
+import static org.junit.Assert.*;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.List;
+
+public class NullClientExceptionMonitorApiTest {
+
+ private NullClientExceptionMonitorApi exceptionMonitorApi;
+
+ @Before
+ public void setUp() {
+ exceptionMonitorApi = new NullClientExceptionMonitorApi();
+ }
+
+ @Test
+ public void testGetApolloConfigExceptionList() {
+ List exceptionList = exceptionMonitorApi.getApolloConfigExceptionList();
+
+ assertNotNull(exceptionList);
+ assertTrue(exceptionList.isEmpty());
+ }
+
+ @Test
+ public void testGetApolloConfigExceptionDetails() {
+ List exceptionDetails = exceptionMonitorApi.getApolloConfigExceptionDetails();
+
+ assertNotNull(exceptionDetails);
+ assertTrue(exceptionDetails.isEmpty());
+ }
+}
diff --git a/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/NullClientNamespaceMonitorApiTest.java b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/NullClientNamespaceMonitorApiTest.java
new file mode 100644
index 00000000..d44645f4
--- /dev/null
+++ b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/NullClientNamespaceMonitorApiTest.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.listener.impl;
+
+import static org.junit.Assert.*;
+
+import com.ctrip.framework.apollo.monitor.api.ApolloClientNamespaceMonitorApi.NamespaceMetrics;
+import java.util.Set;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.List;
+import java.util.Map;
+
+public class NullClientNamespaceMonitorApiTest {
+
+ private NullClientNamespaceMonitorApi namespaceMonitorApi;
+
+ @Before
+ public void setUp() {
+ namespaceMonitorApi = new NullClientNamespaceMonitorApi();
+ }
+
+ @Test
+ public void testGetNamespaceMetrics() {
+ Map metrics = namespaceMonitorApi.getNamespaceMetrics();
+
+ assertNotNull(metrics);
+ assertTrue(metrics.isEmpty());
+ }
+
+ @Test
+ public void testGetNamespaceItemNames() {
+ Integer testNamespace = namespaceMonitorApi.getNamespacePropertySize("testNamespace");
+ assertEquals(0, testNamespace.intValue());
+
+ }
+
+ @Test
+ public void testGetNotFoundNamespaces() {
+ List notFoundNamespaces = namespaceMonitorApi.getNotFoundNamespaces();
+
+ assertNotNull(notFoundNamespaces);
+ assertTrue(notFoundNamespaces.isEmpty());
+ }
+
+ @Test
+ public void testGetTimeoutNamespaces() {
+ List timeoutNamespaces = namespaceMonitorApi.getTimeoutNamespaces();
+
+ assertNotNull(timeoutNamespaces);
+ assertTrue(timeoutNamespaces.isEmpty());
+ }
+}
diff --git a/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/NullClientThreadPoolMonitorApiTest.java b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/NullClientThreadPoolMonitorApiTest.java
new file mode 100644
index 00000000..81f812c7
--- /dev/null
+++ b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/listener/impl/NullClientThreadPoolMonitorApiTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.listener.impl;
+
+import static org.junit.Assert.*;
+
+import com.ctrip.framework.apollo.monitor.api.ApolloClientThreadPoolMonitorApi.ApolloThreadPoolInfo;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Map;
+
+public class NullClientThreadPoolMonitorApiTest {
+
+ private NullClientThreadPoolMonitorApi monitorApi;
+
+ @Before
+ public void setUp() {
+ monitorApi = new NullClientThreadPoolMonitorApi();
+ }
+
+ @Test
+ public void testGetThreadPoolInfo() {
+ Map threadPoolInfo = monitorApi.getThreadPoolInfo();
+
+ assertNotNull(threadPoolInfo);
+ assertTrue(threadPoolInfo.isEmpty());
+ }
+
+ @Test
+ public void testGetRemoteConfigRepositoryThreadPoolInfo() {
+ ApolloThreadPoolInfo info = monitorApi.getRemoteConfigRepositoryThreadPoolInfo();
+ assertNotNull(info);
+ assertEquals(0, info.getPoolSize());
+ }
+
+}
diff --git a/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/tracer/ApolloClientMessageProducerCompositeTest.java b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/tracer/ApolloClientMessageProducerCompositeTest.java
new file mode 100644
index 00000000..b0eb740f
--- /dev/null
+++ b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/tracer/ApolloClientMessageProducerCompositeTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.tracer;
+
+import static org.mockito.Mockito.*;
+import static org.junit.Assert.*;
+
+import com.ctrip.framework.apollo.tracer.spi.MessageProducer;
+import com.ctrip.framework.apollo.tracer.spi.Transaction;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class ApolloClientMessageProducerCompositeTest {
+
+ private ApolloClientMessageProducerComposite composite;
+
+ @Mock
+ private MessageProducer producer1;
+
+ @Mock
+ private MessageProducer producer2;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ List producers = Arrays.asList(producer1, producer2);
+ composite = new ApolloClientMessageProducerComposite(producers);
+ }
+
+ @Test
+ public void testLogError_Throwable() {
+ Throwable cause = new Exception("Test exception");
+
+ composite.logError(cause);
+
+ verify(producer1).logError(cause);
+ verify(producer2).logError(cause);
+ }
+
+ @Test
+ public void testLogError_String_Throwable() {
+ String message = "Test error message";
+ Throwable cause = new Exception("Test exception");
+
+ composite.logError(message, cause);
+
+ verify(producer1).logError(message, cause);
+ verify(producer2).logError(message, cause);
+ }
+
+ @Test
+ public void testLogEvent_Type_Name() {
+ String type = "EVENT_TYPE";
+ String name = "EVENT_NAME";
+
+ composite.logEvent(type, name);
+
+ verify(producer1).logEvent(type, name);
+ verify(producer2).logEvent(type, name);
+ }
+
+ @Test
+ public void testLogEvent_Type_Name_Status_NameValuePairs() {
+ String type = "EVENT_TYPE";
+ String name = "EVENT_NAME";
+ String status = "SUCCESS";
+ String nameValuePairs = "key=value";
+
+ composite.logEvent(type, name, status, nameValuePairs);
+
+ verify(producer1).logEvent(type, name, status, nameValuePairs);
+ verify(producer2).logEvent(type, name, status, nameValuePairs);
+ }
+
+ @Test
+ public void testLogMetricsForCount() {
+ String name = "METRIC_NAME";
+
+ composite.logMetricsForCount(name);
+
+ verify(producer1).logMetricsForCount(name);
+ verify(producer2).logMetricsForCount(name);
+ }
+
+ @Test
+ public void testNewTransaction() {
+ String type = "TRANSACTION_TYPE";
+ String name = "TRANSACTION_NAME";
+
+ Transaction transaction1 = mock(Transaction.class);
+ when(producer1.newTransaction(type, name)).thenReturn(null);
+ when(producer2.newTransaction(type, name)).thenReturn(transaction1);
+
+ Transaction result = composite.newTransaction(type, name);
+
+ assertEquals(transaction1, result);
+ verify(producer1).newTransaction(type, name);
+ verify(producer2).newTransaction(type, name);
+ }
+
+ @Test
+ public void testNewTransaction_NoValidTransaction() {
+ String type = "TRANSACTION_TYPE";
+ String name = "TRANSACTION_NAME";
+
+ when(producer1.newTransaction(type, name)).thenReturn(null);
+ when(producer2.newTransaction(type, name)).thenReturn(null);
+
+ Transaction result = composite.newTransaction(type, name);
+
+ assertEquals(ApolloClientMessageProducerComposite.NULL_TRANSACTION, result);
+ verify(producer1).newTransaction(type, name);
+ verify(producer2).newTransaction(type, name);
+ }
+}
diff --git a/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/tracer/ApolloClientMonitorMessageProducerTest.java b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/tracer/ApolloClientMonitorMessageProducerTest.java
new file mode 100644
index 00000000..dde82909
--- /dev/null
+++ b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/internal/tracer/ApolloClientMonitorMessageProducerTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.tracer;
+
+import static com.ctrip.framework.apollo.monitor.internal.ApolloClientMonitorConstant.*;
+import static org.junit.Assert.*;
+
+import com.ctrip.framework.apollo.tracer.spi.Transaction;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.MockitoAnnotations;
+
+
+public class ApolloClientMonitorMessageProducerTest {
+
+ private ApolloClientMonitorMessageProducer producer;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ producer = new ApolloClientMonitorMessageProducer();
+ }
+
+ @Test
+ public void testLogError_Throwable() {
+ Throwable cause = new Exception("Test exception");
+
+ producer.logError(cause);
+ }
+
+ @Test
+ public void testLogError_String_Throwable() {
+ String message = "Test error message";
+ Throwable cause = new Exception("Test exception");
+
+ producer.logError(message, cause);
+ }
+
+ @Test
+ public void testLogEvent_TaggedEvent() {
+ String type = ApolloClientMonitorMessageProducer.TAGS.get(0); // APOLLO_CLIENT_CONFIGCHANGES
+ String name = "Test event";
+
+ producer.logEvent(type, name);
+ }
+
+ @Test
+ public void testLogEvent_ClientConfigEvent() {
+ String type = APOLLO_CLIENT_CONFIGS + "namespace";
+ String name = "Test config";
+
+ producer.logEvent(type, name);
+ }
+
+ @Test
+ public void testLogMetricsForCount() {
+ String name = APOLLO_CLIENT_NAMESPACE_USAGE + ":testNamespace";
+
+ producer.logMetricsForCount(name);
+ }
+
+ @Test
+ public void testNewTransaction() {
+ Transaction result = producer.newTransaction("type", "name");
+
+ assertEquals(ApolloClientMessageProducerComposite.NULL_TRANSACTION, result);
+ }
+}
\ No newline at end of file
diff --git a/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/stress/ApolloClientMonitorStressTest.java b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/stress/ApolloClientMonitorStressTest.java
new file mode 100644
index 00000000..778bd527
--- /dev/null
+++ b/apollo-client/src/test/java/com/ctrip/framework/apollo/monitor/stress/ApolloClientMonitorStressTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.stress;
+
+import static com.ctrip.framework.apollo.monitor.internal.ApolloClientMonitorConstant.*;
+
+import com.ctrip.framework.apollo.ConfigService;
+import com.ctrip.framework.apollo.monitor.internal.event.ApolloClientMonitorEventFactory;
+import com.ctrip.framework.apollo.monitor.internal.event.ApolloClientMonitorEventPublisher;
+import com.github.noconnor.junitperf.JUnitPerfRule;
+import com.github.noconnor.junitperf.JUnitPerfTest;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+@Ignore("Stress test")
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringBootTest(classes = ApolloClientMonitorStressTest.class)
+public class ApolloClientMonitorStressTest {
+
+ @Rule
+ public JUnitPerfRule perfTestRule = new JUnitPerfRule();
+
+ @Test
+ @JUnitPerfTest(threads = 25, durationMs = 10000, warmUpMs = 1000, maxExecutionsPerSecond = 1000)
+ public void testConfigMonitor() {
+ System.out.println("abcdeft");
+ ConfigService.getConfigMonitor().getExporterData();
+ }
+
+ @Test
+ @JUnitPerfTest(threads = 50, durationMs = 10000, warmUpMs = 1000, maxExecutionsPerSecond = 1000)
+ public void testPublishEvent() {
+ ApolloClientMonitorEventPublisher.publish(
+ ApolloClientMonitorEventFactory.getInstance()
+ .createEvent(APOLLO_CLIENT_NAMESPACE_USAGE)
+ .putAttachment(NAMESPACE, "application"));
+ }
+}
diff --git a/apollo-client/src/test/java/com/ctrip/framework/apollo/util/ConfigUtilTest.java b/apollo-client/src/test/java/com/ctrip/framework/apollo/util/ConfigUtilTest.java
index 23a37362..18cda2dd 100644
--- a/apollo-client/src/test/java/com/ctrip/framework/apollo/util/ConfigUtilTest.java
+++ b/apollo-client/src/test/java/com/ctrip/framework/apollo/util/ConfigUtilTest.java
@@ -255,6 +255,92 @@ public void testCustomizePropertiesOrdered() {
configUtil.isPropertiesOrderEnabled());
}
+ @Test
+ public void testMonitorExternalType() {
+ String someMonitorExternalType = "someType";
+ System.setProperty(ApolloClientSystemConsts.APOLLO_CLIENT_MONITOR_EXTERNAL_TYPE, someMonitorExternalType);
+
+ ConfigUtil configUtil = new ConfigUtil();
+
+ assertEquals(someMonitorExternalType, configUtil.getMonitorExternalType());
+ }
+
+ @Test
+ public void testCustomizeMonitorExternalCollectPeriod() {
+ int somePeriod = 5;
+ System.setProperty(ApolloClientSystemConsts.APOLLO_CLIENT_MONITOR_EXTERNAL_EXPORT_PERIOD, String.valueOf(somePeriod));
+
+ ConfigUtil configUtil = new ConfigUtil();
+
+ assertEquals(somePeriod, configUtil.getMonitorExternalExportPeriod());
+ }
+
+ @Test
+ public void testCustomizeInvalidMonitorExternalCollectPeriod() {
+ String someInvalidPeriod = "a";
+ System.setProperty(ApolloClientSystemConsts.APOLLO_CLIENT_MONITOR_EXTERNAL_EXPORT_PERIOD, someInvalidPeriod);
+
+ ConfigUtil configUtil = new ConfigUtil();
+
+ assertEquals(10, configUtil.getMonitorExternalExportPeriod()); // Default value
+ }
+
+ @Test
+ public void testClientMonitorEnabled() {
+ System.setProperty(ApolloClientSystemConsts.APOLLO_CLIENT_MONITOR_ENABLED, "true");
+
+ ConfigUtil configUtil = new ConfigUtil();
+
+ assertTrue(configUtil.isClientMonitorEnabled());
+ }
+
+ @Test
+ public void testClientMonitorEnabledDefault() {
+ System.clearProperty(ApolloClientSystemConsts.APOLLO_CLIENT_MONITOR_ENABLED);
+
+ ConfigUtil configUtil = new ConfigUtil();
+
+ assertFalse(configUtil.isClientMonitorEnabled()); // Default value
+ }
+
+ @Test
+ public void testClientMonitorJmxEnabled() {
+ System.setProperty(ApolloClientSystemConsts.APOLLO_CLIENT_MONITOR_JMX_ENABLED, "true");
+
+ ConfigUtil configUtil = new ConfigUtil();
+
+ assertTrue(configUtil.isClientMonitorJmxEnabled());
+ }
+
+ @Test
+ public void testClientMonitorJmxEnabledDefault() {
+ System.clearProperty(ApolloClientSystemConsts.APOLLO_CLIENT_MONITOR_JMX_ENABLED);
+
+ ConfigUtil configUtil = new ConfigUtil();
+
+ assertFalse(configUtil.isClientMonitorJmxEnabled()); // Default value
+ }
+
+ @Test
+ public void testCustomizeMonitorExceptionQueueSize() {
+ int someQueueSize = 10;
+ System.setProperty(ApolloClientSystemConsts.APOLLO_CLIENT_MONITOR_EXCEPTION_QUEUE_SIZE, String.valueOf(someQueueSize));
+
+ ConfigUtil configUtil = new ConfigUtil();
+
+ assertEquals(someQueueSize, configUtil.getMonitorExceptionQueueSize());
+ }
+
+ @Test
+ public void testCustomizeInvalidMonitorExceptionQueueSize() {
+ String someInvalidQueueSize = "a";
+ System.setProperty(ApolloClientSystemConsts.APOLLO_CLIENT_MONITOR_EXCEPTION_QUEUE_SIZE, someInvalidQueueSize);
+
+ ConfigUtil configUtil = new ConfigUtil();
+
+ assertEquals(25, configUtil.getMonitorExceptionQueueSize()); // Default value
+ }
+
@Test
public void test() {
ConfigUtil configUtil = new ConfigUtil();
diff --git a/apollo-client/src/test/resources/META-INF/services/com.ctrip.framework.apollo.monitor.internal.exporter.ApolloClientMetricsExporter b/apollo-client/src/test/resources/META-INF/services/com.ctrip.framework.apollo.monitor.internal.exporter.ApolloClientMetricsExporter
new file mode 100644
index 00000000..2deb4099
--- /dev/null
+++ b/apollo-client/src/test/resources/META-INF/services/com.ctrip.framework.apollo.monitor.internal.exporter.ApolloClientMetricsExporter
@@ -0,0 +1 @@
+com.ctrip.framework.apollo.monitor.internal.exporter.MockApolloClientMetricsExporter
\ No newline at end of file
diff --git a/apollo-core/src/main/java/com/ctrip/framework/apollo/core/ApolloClientSystemConsts.java b/apollo-core/src/main/java/com/ctrip/framework/apollo/core/ApolloClientSystemConsts.java
index 0fa0550a..2bf8f850 100644
--- a/apollo-core/src/main/java/com/ctrip/framework/apollo/core/ApolloClientSystemConsts.java
+++ b/apollo-core/src/main/java/com/ctrip/framework/apollo/core/ApolloClientSystemConsts.java
@@ -161,4 +161,28 @@ public class ApolloClientSystemConsts {
* enable apollo overrideSystemProperties
*/
public static final String APOLLO_OVERRIDE_SYSTEM_PROPERTIES = "apollo.override-system-properties";
+
+ /**
+ * apollo client monitor enabled
+ */
+ public static final String APOLLO_CLIENT_MONITOR_ENABLED = "apollo.client.monitor.enabled";
+
+ /**
+ * apollo client monitor exception save size
+ */
+ public static final String APOLLO_CLIENT_MONITOR_EXCEPTION_QUEUE_SIZE = "apollo.client.monitor.exception-queue-size";
+ /**
+ * apollo client monitor jmx enabled
+ */
+ public static final String APOLLO_CLIENT_MONITOR_JMX_ENABLED = "apollo.client.monitor.jmx.enabled";
+
+ /**
+ * apollo client monitor form {such as jmx,prometheus}
+ */
+ public static final String APOLLO_CLIENT_MONITOR_EXTERNAL_TYPE = "apollo.client.monitor.external.type";
+
+ /**
+ * apollo client monitor collect period
+ */
+ public static final String APOLLO_CLIENT_MONITOR_EXTERNAL_EXPORT_PERIOD = "apollo.client.monitor.external.export-period";
}
diff --git a/apollo-core/src/main/java/com/ctrip/framework/apollo/core/ConfigConsts.java b/apollo-core/src/main/java/com/ctrip/framework/apollo/core/ConfigConsts.java
index a6108dfb..19f33278 100644
--- a/apollo-core/src/main/java/com/ctrip/framework/apollo/core/ConfigConsts.java
+++ b/apollo-core/src/main/java/com/ctrip/framework/apollo/core/ConfigConsts.java
@@ -24,5 +24,6 @@ public interface ConfigConsts {
String APOLLO_META_KEY = "apollo.meta";
String CONFIG_FILE_CONTENT_KEY = "content";
String NO_APPID_PLACEHOLDER = "ApolloNoAppIdPlaceHolder";
+ String APOLLO_AUTO_UPDATE_INJECTED_SPRING_PROPERTIES = "ApolloAutoUpdateInjectedSpringProperties";
long NOTIFICATION_ID_PLACEHOLDER = -1;
}
diff --git a/apollo-core/src/main/java/com/ctrip/framework/apollo/tracer/Tracer.java b/apollo-core/src/main/java/com/ctrip/framework/apollo/tracer/Tracer.java
index e97e8a6d..d8971db0 100644
--- a/apollo-core/src/main/java/com/ctrip/framework/apollo/tracer/Tracer.java
+++ b/apollo-core/src/main/java/com/ctrip/framework/apollo/tracer/Tracer.java
@@ -88,6 +88,14 @@ public static void logEvent(String type, String name, String status, String name
type, name, status, nameValuePairs, ex);
}
}
+
+ public static void logMetricsForCount(String name) {
+ try {
+ getProducer().logMetricsForCount(name);
+ } catch (Throwable ex) {
+ logger.warn("Failed to log metrics for count: {}", name, ex);
+ }
+ }
public static Transaction newTransaction(String type, String name) {
try {
diff --git a/apollo-core/src/main/java/com/ctrip/framework/apollo/tracer/internals/cat/CatMessageProducer.java b/apollo-core/src/main/java/com/ctrip/framework/apollo/tracer/internals/cat/CatMessageProducer.java
index cb525f90..e1796073 100644
--- a/apollo-core/src/main/java/com/ctrip/framework/apollo/tracer/internals/cat/CatMessageProducer.java
+++ b/apollo-core/src/main/java/com/ctrip/framework/apollo/tracer/internals/cat/CatMessageProducer.java
@@ -45,6 +45,11 @@ public void logEvent(String type, String name, String status, String nameValuePa
status, nameValuePairs);
}
+ @Override
+ public void logMetricsForCount(String name) {
+ Cat.logMetricForCount(name);
+ }
+
@Override
public Transaction newTransaction(String type, String name) {
return new CatTransaction(Cat.newTransaction(type, name));
diff --git a/apollo-core/src/main/java/com/ctrip/framework/apollo/tracer/internals/cat/CatTransaction.java b/apollo-core/src/main/java/com/ctrip/framework/apollo/tracer/internals/cat/CatTransaction.java
index bccdb5e9..0251c6f8 100644
--- a/apollo-core/src/main/java/com/ctrip/framework/apollo/tracer/internals/cat/CatTransaction.java
+++ b/apollo-core/src/main/java/com/ctrip/framework/apollo/tracer/internals/cat/CatTransaction.java
@@ -50,4 +50,4 @@ public void addData(String key, Object value) {
public void complete() {
catTransaction.complete();
}
-}
+}
\ No newline at end of file
diff --git a/apollo-core/src/main/java/com/ctrip/framework/apollo/tracer/spi/MessageProducer.java b/apollo-core/src/main/java/com/ctrip/framework/apollo/tracer/spi/MessageProducer.java
index 61eb8040..7654c56c 100644
--- a/apollo-core/src/main/java/com/ctrip/framework/apollo/tracer/spi/MessageProducer.java
+++ b/apollo-core/src/main/java/com/ctrip/framework/apollo/tracer/spi/MessageProducer.java
@@ -16,10 +16,12 @@
*/
package com.ctrip.framework.apollo.tracer.spi;
+import com.ctrip.framework.apollo.core.spi.Ordered;
+
/**
* @author Jason Song(song_s@ctrip.com)
*/
-public interface MessageProducer {
+public interface MessageProducer extends Ordered {
/**
* Log an error.
*
@@ -52,6 +54,16 @@ public interface MessageProducer {
*/
void logEvent(String type, String name, String status, String nameValuePairs);
+
+ /**
+ * log metrics for count
+ *
+ * @param name metrics name
+ */
+ default void logMetricsForCount(String name) {
+ //do nothing
+ }
+
/**
* Create a new transaction with given type and name.
*
@@ -59,4 +71,10 @@ public interface MessageProducer {
* @param name transaction name
*/
Transaction newTransaction(String type, String name);
+
+
+ @Override
+ default int getOrder() {
+ return 0;
+ }
}
diff --git a/apollo-plugin/apollo-plugin-client-prometheus/pom.xml b/apollo-plugin/apollo-plugin-client-prometheus/pom.xml
new file mode 100644
index 00000000..07cb99c6
--- /dev/null
+++ b/apollo-plugin/apollo-plugin-client-prometheus/pom.xml
@@ -0,0 +1,45 @@
+
+
+
+ 4.0.0
+
+ apollo-plugin
+ com.ctrip.framework.apollo
+ ${revision}
+ ../pom.xml
+
+
+ apollo-plugin-client-prometheus
+ Apollo Plugin Prometheus
+ jar
+
+
+
+ com.ctrip.framework.apollo
+ apollo-client
+ provided
+
+
+ io.prometheus
+ simpleclient_common
+
+
+
+
\ No newline at end of file
diff --git a/apollo-plugin/apollo-plugin-client-prometheus/src/main/java/com/ctrip/framework/apollo/monitor/internal/exporter/impl/PrometheusApolloClientMetricsExporter.java b/apollo-plugin/apollo-plugin-client-prometheus/src/main/java/com/ctrip/framework/apollo/monitor/internal/exporter/impl/PrometheusApolloClientMetricsExporter.java
new file mode 100644
index 00000000..a07e3209
--- /dev/null
+++ b/apollo-plugin/apollo-plugin-client-prometheus/src/main/java/com/ctrip/framework/apollo/monitor/internal/exporter/impl/PrometheusApolloClientMetricsExporter.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.exporter.impl;
+
+import com.ctrip.framework.apollo.core.utils.DeferredLoggerFactory;
+import com.ctrip.framework.apollo.monitor.internal.exporter.AbstractApolloClientMetricsExporter;
+import com.ctrip.framework.apollo.monitor.internal.exporter.ApolloClientMetricsExporter;
+import com.ctrip.framework.apollo.monitor.internal.listener.impl.DefaultApolloClientNamespaceApi;
+import com.google.common.collect.Maps;
+import io.prometheus.client.Collector;
+import io.prometheus.client.CollectorRegistry;
+import io.prometheus.client.Counter;
+import io.prometheus.client.Gauge;
+import io.prometheus.client.exporter.common.TextFormat;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.util.HashMap;
+import java.util.Map;
+import org.slf4j.Logger;
+
+/**
+ * @author Rawven
+ */
+public class PrometheusApolloClientMetricsExporter extends
+ AbstractApolloClientMetricsExporter implements ApolloClientMetricsExporter {
+
+ private static final String PROMETHEUS = "prometheus";
+ private final Logger logger = DeferredLoggerFactory.getLogger(
+ DefaultApolloClientNamespaceApi.class);
+ protected CollectorRegistry registry;
+ protected Map map;
+
+ @Override
+ public void doInit() {
+ registry = new CollectorRegistry();
+ map = Maps.newConcurrentMap();
+ }
+
+ @Override
+ public boolean isSupport(String form) {
+ return PROMETHEUS.equals(form);
+ }
+
+
+ @Override
+ public void registerOrUpdateCounterSample(String name, Map tags,
+ double incrValue) {
+ Counter counter = (Counter) map.computeIfAbsent(name,
+ key -> createCounter(key, tags));
+ counter.labels(tags.values().toArray(new String[0])).inc(incrValue);
+ }
+
+ private Counter createCounter(String name, Map tags) {
+ return Counter.build()
+ .name(name)
+ .help("apollo counter metrics")
+ .labelNames(tags.keySet().toArray(new String[0]))
+ .register(registry);
+ }
+
+ @Override
+ public void registerOrUpdateGaugeSample(String name, Map tags, double value) {
+ Gauge gauge = (Gauge) map.computeIfAbsent(name, key -> createGauge(key, tags));
+ gauge.labels(tags.values().toArray(new String[0])).set(value);
+ }
+
+ private Gauge createGauge(String name, Map tags) {
+ return Gauge.build()
+ .name(name)
+ .help("apollo gauge metrics")
+ .labelNames(tags.keySet().toArray(new String[0]))
+ .register(registry);
+ }
+
+
+ @Override
+ public String response() {
+ try (StringWriter writer = new StringWriter()) {
+ TextFormat.writeFormat(TextFormat.CONTENT_TYPE_OPENMETRICS_100, writer,
+ registry.metricFamilySamples());
+ return writer.toString();
+ } catch (IOException e) {
+ logger.error("Write metrics to Prometheus format failed", e);
+ return "";
+ }
+ }
+}
+
\ No newline at end of file
diff --git a/apollo-plugin/apollo-plugin-client-prometheus/src/main/resources/META-INF/services/com.ctrip.framework.apollo.monitor.internal.exporter.ApolloClientMetricsExporter b/apollo-plugin/apollo-plugin-client-prometheus/src/main/resources/META-INF/services/com.ctrip.framework.apollo.monitor.internal.exporter.ApolloClientMetricsExporter
new file mode 100644
index 00000000..7296cdce
--- /dev/null
+++ b/apollo-plugin/apollo-plugin-client-prometheus/src/main/resources/META-INF/services/com.ctrip.framework.apollo.monitor.internal.exporter.ApolloClientMetricsExporter
@@ -0,0 +1 @@
+com.ctrip.framework.apollo.monitor.internal.exporter.impl.PrometheusApolloClientMetricsExporter
\ No newline at end of file
diff --git a/apollo-plugin/apollo-plugin-client-prometheus/src/test/java/com/ctrip/framework/apollo/monitor/internal/exporter/impl/PrometheusApolloClientMetricsExporterTest.java b/apollo-plugin/apollo-plugin-client-prometheus/src/test/java/com/ctrip/framework/apollo/monitor/internal/exporter/impl/PrometheusApolloClientMetricsExporterTest.java
new file mode 100644
index 00000000..a0940776
--- /dev/null
+++ b/apollo-plugin/apollo-plugin-client-prometheus/src/test/java/com/ctrip/framework/apollo/monitor/internal/exporter/impl/PrometheusApolloClientMetricsExporterTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2022 Apollo Authors
+ *
+ * Licensed 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 com.ctrip.framework.apollo.monitor.internal.exporter.impl;
+import static org.junit.Assert.*;
+
+import io.prometheus.client.Counter;
+import io.prometheus.client.Gauge;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class PrometheusApolloClientMetricsExporterTest {
+
+ private PrometheusApolloClientMetricsExporter exporter;
+
+ @Before
+ public void setUp() {
+ exporter = new PrometheusApolloClientMetricsExporter();
+ exporter.doInit();
+ }
+
+ @Test
+ public void testIsSupport() {
+ assertTrue(exporter.isSupport("prometheus"));
+ assertFalse(exporter.isSupport("other"));
+ }
+
+ @Test
+ public void testRegisterOrUpdateCounterSample() {
+ String name = "test_counter";
+ Map tags = new HashMap<>();
+ tags.put("tag1", "value1");
+
+ exporter.registerOrUpdateCounterSample(name, tags, 1.0);
+
+ Counter counter = (Counter) exporter.map.get(name);
+
+ assertNotNull(counter);
+ assertEquals(1.0, counter.labels("value1").get(), 0.001);
+ }
+
+ @Test
+ public void testRegisterOrUpdateGaugeSample() {
+ String name = "test_gauge";
+ Map tags = new HashMap<>();
+ tags.put("tag2", "value2");
+
+ exporter.registerOrUpdateGaugeSample(name, tags, 3.0);
+
+ Gauge gauge = (Gauge) exporter.map.get(name);
+
+ assertNotNull(gauge);
+ assertEquals(3.0, gauge.labels("value2").get(), 0.001);
+ }
+
+ @Test
+ public void testResponse() {
+ String response = exporter.response();
+ assertNotNull(response);
+ assertFalse(response.isEmpty());
+ }
+}
diff --git a/apollo-plugin/apollo-plugin-client-prometheus/src/test/resources/META-INF/services/com.ctrip.framework.apollo.monitor.internal.exporter.ApolloClientMetricsExporter b/apollo-plugin/apollo-plugin-client-prometheus/src/test/resources/META-INF/services/com.ctrip.framework.apollo.monitor.internal.exporter.ApolloClientMetricsExporter
new file mode 100644
index 00000000..7296cdce
--- /dev/null
+++ b/apollo-plugin/apollo-plugin-client-prometheus/src/test/resources/META-INF/services/com.ctrip.framework.apollo.monitor.internal.exporter.ApolloClientMetricsExporter
@@ -0,0 +1 @@
+com.ctrip.framework.apollo.monitor.internal.exporter.impl.PrometheusApolloClientMetricsExporter
\ No newline at end of file
diff --git a/apollo-plugin/pom.xml b/apollo-plugin/pom.xml
index 3a47b738..f0d1b00f 100644
--- a/apollo-plugin/pom.xml
+++ b/apollo-plugin/pom.xml
@@ -31,6 +31,7 @@
apollo-plugin-log4j2
+ apollo-plugin-client-prometheus
diff --git a/pom.xml b/pom.xml
index d153bf11..31321a3c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -65,6 +65,7 @@
UTF-8
2.7.18
3.1.0
+ 1.36.0
3.10.1
2.22.2
@@ -136,6 +137,12 @@
5.7.0
test
+
+ com.github.noconnor
+ junitperf
+ ${junitperf}
+ test
+
com.github.stefanbirkner
system-lambda