From f932503db93aa5e2e5a33d2d5b0a2da251323ecd Mon Sep 17 00:00:00 2001 From: lilai Date: Mon, 17 Jun 2024 10:53:37 +0800 Subject: [PATCH] reduce startup time Signed-off-by: lilai --- .../config/config.properties | 6 ++ .../config/test/config.properties | 6 ++ .../core/classloader/ClassLoaderManager.java | 4 +- .../sermant/core/common/BootArgsIndexer.java | 23 ++--- .../io/sermant/core/config/ConfigManager.java | 2 +- .../config/strategy/LoadConfigStrategy.java | 16 ++-- .../core/config/utils/ConfigFieldUtil.java | 17 ++-- .../core/config/utils/ConfigValueUtil.java | 86 +++++++++++-------- .../io/sermant/core/plugin/PluginManager.java | 16 +--- .../core/plugin/PluginSystemEntrance.java | 15 +--- .../plugin/agent/BufferedAgentBuilder.java | 10 +++ .../core/plugin/agent/ByteEnhanceManager.java | 15 ++++ .../agent/collector/PluginCollector.java | 16 +++- .../core/plugin/agent/config/AgentConfig.java | 39 ++++++++- .../ClassLoaderLoadClassInterceptor.java | 2 +- .../plugin/classloader/PluginClassFinder.java | 3 +- .../plugin/classloader/PluginClassLoader.java | 1 - .../plugin/config/PluginConfigManager.java | 15 ++-- .../sermant/core/service/ServiceManager.java | 10 ++- .../java/io/sermant/core/utils/FileUtils.java | 72 ++++++++++++++++ .../god/common/SermantClassLoader.java | 3 +- .../config/LoadPropertiesStrategy.java | 19 ++-- .../implement/config/LoadYamlStrategy.java | 14 +-- .../io/sermant/premain/AgentLauncher.java | 5 +- .../io/sermant/premain/utils/LoggerUtils.java | 18 ++++ 25 files changed, 291 insertions(+), 142 deletions(-) diff --git a/sermant-agentcore/sermant-agentcore-config/config/config.properties b/sermant-agentcore/sermant-agentcore-config/config/config.properties index 73ada350b2..5b309ba2cd 100644 --- a/sermant-agentcore/sermant-agentcore-config/config/config.properties +++ b/sermant-agentcore/sermant-agentcore-config/config/config.properties @@ -15,6 +15,12 @@ agent.config.ignoredPrefixes=io.sermant agent.config.ignoredInterfaces=org.springframework.cglib.proxy.Factory # Specifies which classes in the plugins are allowed to be bytecode enhanced (classes in the plugins are not allowed to be bytecode enhanced by default) agent.config.serviceInjectList=io.sermant.discovery.service.lb.filter.NopInstanceFilter,io.sermant.discovery.service.lb.DiscoveryManager,io.sermant.discovery.service.util.ApplyUtil,io.sermant.discovery.service.lb.cache.InstanceCacheManager +# Generate unmatched class name to file, used to reduce startup time for the second time and after +agent.config.preFilter.enable=false +# Path of unmatched class name file, the default path is the same directory of sermant-agent.jar +agent.config.preFilter.path= +# File name of unmatched class name, the default file is 'unmatched_class_name.txt' +agent.config.preFilter.file= #============================= core service configuration =============================# # Heartbeat service switch agent.service.heartbeat.enable=false diff --git a/sermant-agentcore/sermant-agentcore-config/config/test/config.properties b/sermant-agentcore/sermant-agentcore-config/config/test/config.properties index feb9f071ad..65c47396b7 100644 --- a/sermant-agentcore/sermant-agentcore-config/config/test/config.properties +++ b/sermant-agentcore/sermant-agentcore-config/config/test/config.properties @@ -15,6 +15,12 @@ agent.config.ignoredPrefixes=io.sermant agent.config.ignoredInterfaces=org.springframework.cglib.proxy.Factory # Specifies which classes in the plugins are allowed to be bytecode enhanced (classes in the plugins are not allowed to be bytecode enhanced by default) agent.config.serviceInjectList=io.sermant.discovery.service.lb.filter.NopInstanceFilter,io.sermant.discovery.service.lb.DiscoveryManager,io.sermant.discovery.service.util.ApplyUtil,io.sermant.discovery.service.lb.cache.InstanceCacheManager +# Generate unmatched class name to file, used to reduce startup time for the second time and after +agent.config.preFilter.enable=false +# Path of unmatched class name file, the default path is the same directory of sermant-agent.jar +agent.config.preFilter.path= +# File name of unmatched class name, the default file is 'unmatched_class_name.txt' +agent.config.preFilter.file= #============================= core service configuration =============================# # Heartbeat service switch agent.service.heartbeat.enable=true diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/classloader/ClassLoaderManager.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/classloader/ClassLoaderManager.java index 4e6d1fa771..c7b2c65835 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/classloader/ClassLoaderManager.java +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/classloader/ClassLoaderManager.java @@ -121,7 +121,7 @@ private static URL[] listCoreImplementUrls(String coreImplementPath) throws Malf return urlList.toArray(new URL[0]); } - private static URL[] listCommonLibUrls(String commonLibPath) throws MalformedURLException { + private static List listCommonLibUrls(String commonLibPath) throws MalformedURLException { File commonLibDir = new File(FileUtils.validatePath(commonLibPath)); if (!commonLibDir.exists() || !commonLibDir.isDirectory()) { throw new FileCheckException("common lib is not exist or is not directory."); @@ -134,6 +134,6 @@ private static URL[] listCommonLibUrls(String commonLibPath) throws MalformedURL for (File jar : jars) { urlList.add(jar.toURI().toURL()); } - return urlList.toArray(new URL[0]); + return urlList; } } diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/common/BootArgsIndexer.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/common/BootArgsIndexer.java index 1e08940598..3e2e4c6455 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/common/BootArgsIndexer.java +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/common/BootArgsIndexer.java @@ -128,26 +128,26 @@ public static String getInstanceId() { */ public static void build(Map argsMap) { implementDir = new File(FileUtils.validatePath(argsMap.get(CommonConstant.CORE_IMPLEMENT_DIR_KEY).toString())); - if (!implementDir.exists() || !implementDir.isDirectory()) { + if (!implementDir.isDirectory()) { LOGGER.warning("Implement directory not found! "); } configFile = new File(FileUtils.validatePath(argsMap.get(CommonConstant.CORE_CONFIG_FILE_KEY).toString())); - if (!configFile.exists() || !configFile.isFile()) { + if (!configFile.isFile()) { LOGGER.warning("Config file is not found! "); } pluginSettingFile = new File(FileUtils.validatePath(argsMap.get(CommonConstant.PLUGIN_SETTING_FILE_KEY) .toString())); - if (!pluginSettingFile.exists() || !pluginSettingFile.isFile()) { + if (!pluginSettingFile.isFile()) { LOGGER.warning("Plugin setting file is not found! "); } logSettingFile = new File(FileUtils.validatePath(argsMap.get(CommonConstant.LOG_SETTING_FILE_KEY) .toString())); - if (!logSettingFile.exists() || !logSettingFile.isFile()) { + if (!logSettingFile.isFile()) { LOGGER.warning("Log setting file is not found! Using default log setting file in resources."); } pluginPackageDir = new File(FileUtils.validatePath(argsMap.get(CommonConstant.PLUGIN_PACKAGE_DIR_KEY) .toString())); - if (!pluginPackageDir.exists() || !pluginPackageDir.isDirectory()) { + if (!pluginPackageDir.isDirectory()) { LOGGER.warning("Plugin package directory is not found! "); } @@ -162,20 +162,11 @@ public static void build(Map argsMap) { static { final String currentFile = BootArgsIndexer.class.getProtectionDomain().getCodeSource().getLocation().getPath(); - JarFile jarFile = null; - try { - jarFile = new JarFile(currentFile); + try (JarFile jarFile = new JarFile(currentFile)) { CORE_VERSION = JarFileUtils.getManifestAttr(jarFile, CommonConstant.CORE_VERSION_KEY).toString(); } catch (IOException e) { + LOGGER.severe("Failed to read the core version from the manifest file: " + currentFile); throw new SchemaException(SchemaException.MISSING_VERSION, currentFile); - } finally { - if (jarFile != null) { - try { - jarFile.close(); - } catch (IOException ignored) { - LOGGER.warning("Unexpected exception occurs. "); - } - } } } } diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/config/ConfigManager.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/config/ConfigManager.java index 51bcc555cd..6dcb043ac4 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/config/ConfigManager.java +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/config/ConfigManager.java @@ -111,7 +111,7 @@ public static synchronized void initialize(Map args) { * @param classLoader classLoader, which determines from which class Loader api operations are performed */ protected static void loadConfig(File configFile, ClassLoader classLoader) { - if (configFile.exists() && configFile.isFile()) { + if (configFile.isFile()) { doLoadConfig(configFile, classLoader); } else { loadDefaultConfig(classLoader); diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/config/strategy/LoadConfigStrategy.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/config/strategy/LoadConfigStrategy.java index d4f51cd82e..8e63a22bad 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/config/strategy/LoadConfigStrategy.java +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/config/strategy/LoadConfigStrategy.java @@ -20,7 +20,6 @@ import io.sermant.core.config.common.BaseConfig; import java.io.File; -import java.util.Locale; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; @@ -67,11 +66,12 @@ public interface LoadConfigStrategy { T getConfigHolder(File config, Map argsMap); /** - * Loading configuration: Loads the configuration information of the main carrier object to the configuration object + * Loading configuration: Loads the configuration information of the main carrier object to the configuration + * object * * @param holder configuration holder * @param config configuration object - * @param configuration class type + * @param configuration class type * @param isDynamic is the config loaded dynamically * @return configuration object after processing */ @@ -84,7 +84,7 @@ public interface LoadConfigStrategy { */ class DefaultLoadConfigStrategy implements LoadConfigStrategy { /** - * 日志 + * logger */ private static final Logger LOGGER = LoggerFactory.getLogger(); @@ -95,15 +95,15 @@ public boolean canLoad(File file) { @Override public Object getConfigHolder(File config, Map argsMap) { - LOGGER.log(Level.WARNING, String.format(Locale.ROOT, "[%s] will do nothing when reading config file. ", - DefaultLoadConfigStrategy.class.getName())); + LOGGER.log(Level.WARNING, "[{0}] will do nothing when reading config file. ", + DefaultLoadConfigStrategy.class.getName()); return argsMap; } @Override public R loadConfig(Object holder, R config, boolean isDynamic) { - LOGGER.log(Level.WARNING, String.format(Locale.ROOT, "[%s] will do nothing when loading config. ", - DefaultLoadConfigStrategy.class.getName())); + LOGGER.log(Level.WARNING, "[{0}] will do nothing when loading config. ", + DefaultLoadConfigStrategy.class.getName()); return config; } } diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/config/utils/ConfigFieldUtil.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/config/utils/ConfigFieldUtil.java index 22a5633091..131b109d5c 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/config/utils/ConfigFieldUtil.java +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/config/utils/ConfigFieldUtil.java @@ -80,13 +80,12 @@ public static void setField(Object obj, Field field, Object value) { field.set(obj, value); } } catch (IllegalAccessException ignored) { - LOGGER.log(Level.WARNING, String.format(Locale.ROOT, - "Cannot access field [%s] of [%s], try forced set.", - field.getName(), field.getDeclaringClass().getName())); + LOGGER.log(Level.WARNING, "Cannot access field [%s] of [%s], try forced set.", + new String[]{field.getName(), field.getDeclaringClass().getName()}); forceSet(obj, field, value); } catch (InvocationTargetException ignored) { - LOGGER.log(Level.WARNING, String.format(Locale.ROOT, - "Failed to set field [%s] of [%s].", field.getName(), field.getDeclaringClass().getName())); + LOGGER.log(Level.WARNING, "Failed to set field [%s] of [%s].", + new String[]{field.getName(), field.getDeclaringClass().getName()}); } } @@ -124,11 +123,11 @@ private static void forceSet(Object obj, Field field, Object value) { field.set(obj, value); modifiersField.setInt(field, modifiers); } catch (NoSuchFieldException e) { - LOGGER.log(Level.WARNING, String.format(Locale.ROOT, - "Missing modifiers field [%s] of [%s]. ", field.getName(), field.getDeclaringClass().getName())); + LOGGER.log(Level.WARNING, "Missing modifiers field [{0}] of [{1}]. ", + new String[]{field.getName(), field.getDeclaringClass().getName()}); } catch (IllegalAccessException e) { - LOGGER.log(Level.WARNING, String.format(Locale.ROOT, - "Forced set field [%s] of [%s] failed.", field.getName(), field.getDeclaringClass().getName())); + LOGGER.log(Level.WARNING, "Forced set field [%s] of [%s] failed.", + new String[]{field.getName(), field.getDeclaringClass().getName()}); } } diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/config/utils/ConfigValueUtil.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/config/utils/ConfigValueUtil.java index 9eba8a64f9..c6b810425b 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/config/utils/ConfigValueUtil.java +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/config/utils/ConfigValueUtil.java @@ -96,6 +96,23 @@ public class ConfigValueUtil { key -> key.toLowerCase(Locale.ROOT).replace('.', '-') }; + private static final Map, BaseTypeParser> BASE_TYPE_PARSERS = new HashMap, BaseTypeParser>() { + { + put(int.class, Integer::parseInt); + put(Integer.class, Integer::valueOf); + put(short.class, Short::parseShort); + put(Short.class, Short::valueOf); + put(long.class, Long::parseLong); + put(Long.class, Long::valueOf); + put(float.class, Float::parseFloat); + put(Float.class, Float::valueOf); + put(double.class, Double::parseDouble); + put(Double.class, Double::valueOf); + put(boolean.class, Boolean::parseBoolean); + put(Boolean.class, Boolean::valueOf); + } + }; + /** * function for obtaining reference config values *

Priority: Startup Configuration > Environment Variables > Startup Parameters > Configuration File

@@ -245,8 +262,8 @@ public static Map toMapType(String configStr, Class keyType, Cla } /** - * Converts configuration information strings to int, short, long, float, double, enumeration, String, and - * Object, and returns null if conversion fails + * Converts configuration information strings to int, short, long, float, double, enumeration, String, and Object, + * and returns null if conversion fails * * @param configStr configuration information string * @param type type of object @@ -258,38 +275,19 @@ public static R toBaseType(String configStr, Class type) { if (configStr == null || "".equals(configStr)) { return (R) result; } - if (type == int.class) { - result = Integer.parseInt(configStr); - } else if (type == Integer.class) { - result = Integer.valueOf(configStr); - } else if (type == short.class) { - result = Short.parseShort(configStr); - } else if (type == Short.class) { - result = Short.valueOf(configStr); - } else if (type == long.class) { - result = Long.parseLong(configStr); - } else if (type == Long.class) { - result = Long.valueOf(configStr); - } else if (type == float.class) { - result = Float.parseFloat(configStr); - } else if (type == Float.class) { - result = Float.valueOf(configStr); - } else if (type == double.class) { - result = Double.parseDouble(configStr); - } else if (type == Double.class) { - result = Double.valueOf(configStr); - } else if (type == boolean.class) { - result = Boolean.parseBoolean(configStr); - } else if (type == Boolean.class) { - result = Boolean.valueOf(configStr); - } else if (type.isEnum()) { - result = Enum.valueOf((Class) type, configStr); - } else if (type == String.class || type == Object.class) { - result = configStr; - } else { - result = null; + + BaseTypeParser baseTypeParser = BASE_TYPE_PARSERS.get(type); + if (baseTypeParser != null) { + return (R) baseTypeParser.parse(configStr); } - return (R) result; + + if (type.isEnum()) { + return (R) Enum.valueOf((Class) type, configStr); + } + if (type == String.class || type == Object.class) { + return (R) configStr; + } + return null; } /** @@ -378,8 +376,8 @@ private static String getValByFixedKey(String key, String configVal, Map Environment Variables > Startup Parameters > Configuration File + * Environment variables are read after being processed by KeyFormatter. The priorities are: Startup Configuration > + * Environment Variables > Startup Parameters > Configuration File * * @param key key * @param argsMap argsMap @@ -500,4 +498,22 @@ public interface FixedValueProvider { */ String getFixedValue(String key); } + + /** + * base type parser + * + * @param + * @author lilai + * @since 2024-06-14 + */ + @FunctionalInterface + interface BaseTypeParser { + /** + * parse string to base type such as int, double + * + * @param str original string + * @return parsed object + */ + R parse(String str); + } } diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/PluginManager.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/PluginManager.java index edae3e268e..1d608b3401 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/PluginManager.java +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/PluginManager.java @@ -155,7 +155,7 @@ public static void uninstallAll() { * @param isDynamic Whether the plugin is dynamic */ public static void initPlugins(Set pluginNames, boolean isDynamic) { - if (pluginNames == null || pluginNames.isEmpty()) { + if (CollectionUtils.isEmpty(pluginNames)) { LOGGER.log(Level.WARNING, "Non plugin is configured to be initialized."); return; } @@ -300,9 +300,7 @@ private static Optional toUrl(File file) { */ private static boolean processByJarFile(String pluginName, File jar, boolean ifCheckSchema, JarFileConsumer consumer) { - JarFile jarFile = null; - try { - jarFile = new JarFile(jar); + try (JarFile jarFile = new JarFile(jar)) { if (ifCheckSchema && !PluginSchemaValidator.checkSchema(pluginName, getRealPluginName(pluginName), jarFile)) { throw new SchemaException(SchemaException.UNEXPECTED_EXT_JAR, jar.getPath()); @@ -314,14 +312,6 @@ private static boolean processByJarFile(String pluginName, File jar, boolean ifC } catch (IOException ignored) { LOGGER.warning(String.format(Locale.ROOT, "Check schema of %s failed. ", jar.getPath())); return false; - } finally { - if (jarFile != null) { - try { - jarFile.close(); - } catch (IOException ioException) { - LOGGER.severe("Occurred ioException when close jar."); - } - } } } @@ -344,7 +334,7 @@ private static String getRealPluginName(String pluginName) { * @return all jars */ private static File[] listJars(File dir) { - if (!dir.exists() || !dir.isDirectory()) { + if (!dir.isDirectory()) { return new File[0]; } final File[] files = dir.listFiles(new FileFilter() { diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/PluginSystemEntrance.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/PluginSystemEntrance.java index 02fee9bbf1..8660aea13c 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/PluginSystemEntrance.java +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/PluginSystemEntrance.java @@ -93,24 +93,15 @@ public static void initialize(boolean isDynamic) { * @return Plugin configuration */ private static PluginSetting loadSetting() { - Reader reader = null; - try { - reader = new InputStreamReader(Files.newInputStream(BootArgsIndexer.getPluginSettingFile().toPath()), - CommonConstant.DEFAULT_CHARSET); + try (Reader reader = new InputStreamReader( + Files.newInputStream(BootArgsIndexer.getPluginSettingFile().toPath()), + CommonConstant.DEFAULT_CHARSET)) { Optional pluginSettingOptional = OperationManager.getOperation(YamlConverter.class) .convert(reader, PluginSetting.class); return pluginSettingOptional.orElse(null); } catch (IOException ignored) { LOGGER.warning("Plugin setting file is not found. "); return new PluginSetting(); - } finally { - if (reader != null) { - try { - reader.close(); - } catch (IOException ignored) { - LOGGER.warning("Unexpected exception occurs. "); - } - } } } } diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/agent/BufferedAgentBuilder.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/agent/BufferedAgentBuilder.java index cff6039313..b9722421fa 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/agent/BufferedAgentBuilder.java +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/agent/BufferedAgentBuilder.java @@ -56,6 +56,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; @@ -327,6 +328,11 @@ private static class IgnoredMatcher implements AgentBuilder.RawMatcher { private final Set ignoredInterfaces; + /** + * unMatched Class Cache + */ + private final Map unMatchedClassCache = FileUtils.getUnMatchedClassCache(); + IgnoredMatcher(AgentConfig config) { ignoredPrefixes = config.getIgnoredPrefixes(); serviceInjectList = config.getServiceInjectList(); @@ -336,6 +342,10 @@ private static class IgnoredMatcher implements AgentBuilder.RawMatcher { @Override public boolean matches(TypeDescription typeDesc, ClassLoader classLoader, JavaModule javaModule, Class classBeingRedefined, ProtectionDomain protectionDomain) { + if (unMatchedClassCache.containsKey(typeDesc.getActualName())) { + return true; + } + if (!checkInjectList(typeDesc, classLoader)) { return false; } diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/agent/ByteEnhanceManager.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/agent/ByteEnhanceManager.java index 2f4f348cb8..0b7b0cefca 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/agent/ByteEnhanceManager.java +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/agent/ByteEnhanceManager.java @@ -19,9 +19,11 @@ import io.sermant.core.config.ConfigManager; import io.sermant.core.plugin.Plugin; import io.sermant.core.plugin.agent.collector.PluginCollector; +import io.sermant.core.plugin.agent.config.AgentConfig; import io.sermant.core.plugin.agent.declarer.PluginDescription; import io.sermant.core.plugin.agent.enhance.ClassLoaderDeclarer; import io.sermant.core.service.ServiceConfig; +import io.sermant.core.utils.FileUtils; import net.bytebuddy.agent.builder.AgentBuilder; import net.bytebuddy.agent.builder.AgentBuilder.RedefinitionStrategy; @@ -65,7 +67,20 @@ public static void init(Instrumentation instrumentation) { * Install classloader enhanced bytecode for premain only */ public static void enhance() { + cacheUnmatchedClass(); builder.install(instrumentationCache); + saveUnMatchedClass(); + } + + private static void saveUnMatchedClass() { + if (ConfigManager.getConfig(AgentConfig.class).isPreFilterEnable()) { + Runtime.getRuntime().addShutdownHook(new Thread(FileUtils::writeUnmatchedClassNameToFile + )); + } + } + + private static void cacheUnmatchedClass() { + FileUtils.readUnmatchedClassNameFromFile(); } /** diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/agent/collector/PluginCollector.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/agent/collector/PluginCollector.java index 539925d4c4..52616e245a 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/agent/collector/PluginCollector.java +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/agent/collector/PluginCollector.java @@ -17,7 +17,9 @@ package io.sermant.core.plugin.agent.collector; import io.sermant.core.common.LoggerFactory; +import io.sermant.core.config.ConfigManager; import io.sermant.core.plugin.Plugin; +import io.sermant.core.plugin.agent.config.AgentConfig; import io.sermant.core.plugin.agent.declarer.AbstractPluginDescription; import io.sermant.core.plugin.agent.declarer.InterceptDeclarer; import io.sermant.core.plugin.agent.declarer.PluginDeclarer; @@ -26,6 +28,7 @@ import io.sermant.core.plugin.agent.matcher.ClassTypeMatcher; import io.sermant.core.plugin.agent.transformer.ReentrantTransformer; import io.sermant.core.plugin.classloader.PluginClassLoader; +import io.sermant.core.utils.FileUtils; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.dynamic.DynamicType.Builder; @@ -52,12 +55,14 @@ public class PluginCollector { private static final Logger LOGGER = LoggerFactory.getLogger(); + private static final boolean IS_PRE_FILTER_ENABLE = ConfigManager.getConfig(AgentConfig.class).isPreFilterEnable(); + private PluginCollector() { } /** - * Resolve all plugin descriptions from the plugin collector and apply the configured merge policy to the - * plugin declarers + * Resolve all plugin descriptions from the plugin collector and apply the configured merge policy to the plugin + * declarers * * @param plugin plugin * @return list of PluginDescription @@ -192,7 +197,12 @@ private static Iterable loadDescriptions(ClassLoade private static boolean matchTarget(ElementMatcher matcher, TypeDescription target) { try { - return matcher.matches(target); + boolean result = matcher.matches(target); + if (IS_PRE_FILTER_ENABLE && !result) { + FileUtils.addToUnmatchedClassCache(target.getActualName()); + } + + return result; } catch (Exception exception) { LOGGER.log(Level.WARNING, "Exception occurs when math target: " + target.getActualName() + ",{0}", exception.getMessage()); diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/agent/config/AgentConfig.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/agent/config/AgentConfig.java index 2ce8709ed3..b62d5b7372 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/agent/config/AgentConfig.java +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/agent/config/AgentConfig.java @@ -17,6 +17,7 @@ package io.sermant.core.plugin.agent.config; import io.sermant.core.config.common.BaseConfig; +import io.sermant.core.config.common.ConfigFieldKey; import io.sermant.core.config.common.ConfigTypeKey; import java.util.Collections; @@ -69,10 +70,18 @@ public class AgentConfig implements BaseConfig { */ private Set serviceInjectList = Collections.emptySet(); + @ConfigFieldKey("preFilter.enable") + private boolean preFilterEnable = false; + + @ConfigFieldKey("preFilter.path") + private String preFilterPath; + + @ConfigFieldKey("preFilter.file") + private String preFilterFile; + /** - * Allows classes to be loaded from the thread context, mainly used by the PluginClassLoader to load the - * classes of the host instance through the thread context, if not allowed can be specified during the - * interceptor call + * Allows classes to be loaded from the thread context, mainly used by the PluginClassLoader to load the classes of + * the host instance through the thread context, if not allowed can be specified during the interceptor call */ private boolean useContextLoader = false; @@ -139,4 +148,28 @@ public boolean isUseContextLoader() { public void setUseContextLoader(boolean useContextLoader) { this.useContextLoader = useContextLoader; } + + public boolean isPreFilterEnable() { + return preFilterEnable; + } + + public void setPreFilterEnable(boolean preFilterEnable) { + this.preFilterEnable = preFilterEnable; + } + + public String getPreFilterPath() { + return preFilterPath; + } + + public void setPreFilterPath(String preFilterPath) { + this.preFilterPath = preFilterPath; + } + + public String getPreFilterFile() { + return preFilterFile; + } + + public void setPreFilterFile(String preFilterFile) { + this.preFilterFile = preFilterFile; + } } diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/agent/enhance/ClassLoaderLoadClassInterceptor.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/agent/enhance/ClassLoaderLoadClassInterceptor.java index 3e55ab0e74..287f0be05d 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/agent/enhance/ClassLoaderLoadClassInterceptor.java +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/agent/enhance/ClassLoaderLoadClassInterceptor.java @@ -61,7 +61,7 @@ public ExecuteContext onThrow(ExecuteContext context) throws Exception { if (isSermantClass(name)) { try { Class sermantClazz = ClassLoaderManager.getPluginClassFinder().loadSermantClass(name); - LOGGER.log(Level.INFO, "Load class: {0} successfully by sermant.", name); + LOGGER.log(Level.FINE, "Load class: {0} successfully by sermant.", name); context.changeResult(sermantClazz); context.changeThrowable(null); } catch (ClassNotFoundException classNotFoundException) { diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/classloader/PluginClassFinder.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/classloader/PluginClassFinder.java index 11c1a9f282..fb04abeadf 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/classloader/PluginClassFinder.java +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/classloader/PluginClassFinder.java @@ -23,7 +23,6 @@ import java.util.HashMap; import java.util.Map; import java.util.Optional; -import java.util.logging.Level; import java.util.logging.Logger; /** @@ -74,7 +73,7 @@ public Class loadSermantClass(String name) throws ClassNotFoundException { return clazz; } } catch (ClassNotFoundException e) { - LOGGER.log(Level.WARNING, "load sermant class failed, msg is {0}", e.getMessage()); + // Class not found, ignored, exception thrown later } } throw new ClassNotFoundException("Can not load class in pluginClassLoaders: " + name); diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/classloader/PluginClassLoader.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/classloader/PluginClassLoader.java index e930e7c6d1..6c74d00c77 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/classloader/PluginClassLoader.java +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/classloader/PluginClassLoader.java @@ -167,7 +167,6 @@ public Class loadSermantClass(String name) throws ClassNotFoundException { clazz = super.loadClass(name, false); } catch (ClassNotFoundException e) { // Class not found, ignored, exception thrown later - LOGGER.log(Level.WARNING, "load sermant class failed, msg is {0}", e.getMessage()); } } diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/config/PluginConfigManager.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/config/PluginConfigManager.java index c6f0c2f907..8108dcfd49 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/config/PluginConfigManager.java +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/plugin/config/PluginConfigManager.java @@ -25,9 +25,9 @@ import java.io.File; import java.util.HashMap; -import java.util.Locale; import java.util.Map; import java.util.ServiceLoader; +import java.util.logging.Level; import java.util.logging.Logger; /** @@ -65,17 +65,18 @@ public static void loadPluginConfigs(Plugin plugin) { String pluginConfigKey = ConfigKeyUtil.getTypeKeyWithClassloader(ConfigKeyUtil.getTypeKey(pluginConfigCls), pluginConfigCls.getClassLoader()); final BaseConfig retainedConfig = PLUGIN_CONFIG_MAP.get(pluginConfigKey); - if (pluginConfigFile.exists() && pluginConfigFile.isFile()) { + if (pluginConfigFile.isFile()) { if (retainedConfig == null) { PLUGIN_CONFIG_MAP.put(pluginConfigKey, ConfigManager.doLoad(pluginConfigFile, config, plugin.isDynamic())); plugin.getConfigs().add(pluginConfigKey); } else if (retainedConfig.getClass() == pluginConfigCls) { - LOGGER.fine(String.format(Locale.ROOT, "Skip load config [%s] repeatedly. ", - pluginConfigCls.getName())); + LOGGER.log(Level.FINE, "Skip load config [{0}] repeatedly. ", + pluginConfigCls.getName()); } else { - LOGGER.warning(String.format(Locale.ROOT, "Type key of %s is %s, same as %s's. ", - pluginConfigCls.getName(), pluginConfigKey, retainedConfig.getClass().getName())); + LOGGER.log(Level.WARNING, "Type key of {0} is {1}, same as {2}'s. ", + new String[]{pluginConfigCls.getName(), pluginConfigKey, + retainedConfig.getClass().getName()}); } continue; } @@ -83,7 +84,7 @@ public static void loadPluginConfigs(Plugin plugin) { continue; } - // 不能从文件加载,则为默认配置 + // If it cannot be loaded from file, it is the default configuration PLUGIN_CONFIG_MAP.put(pluginConfigKey, config); plugin.getConfigs().add(pluginConfigKey); } diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/ServiceManager.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/ServiceManager.java index e5f8d193d4..d3cfa32563 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/ServiceManager.java +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/service/ServiceManager.java @@ -34,6 +34,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.ServiceLoader; import java.util.Set; @@ -111,13 +112,14 @@ protected ServiceManager() { */ public static void initServices() { ServiceConfig serviceConfig = ConfigManager.getConfig(ServiceConfig.class); - ArrayList startServiceArray = new ArrayList<>(); + List startServiceArray = new ArrayList<>(); for (final BaseService service : ServiceLoader.load(BaseService.class, ClassLoaderManager.getFrameworkClassLoader())) { - if (serviceConfig.checkServiceEnable(service.getClass().getName()) && loadService(service, + String serviceName = service.getClass().getName(); + if (serviceConfig.checkServiceEnable(serviceName) && loadService(service, service.getClass(), BaseService.class)) { service.start(); - startServiceArray.add(service.getClass().getName()); + startServiceArray.add(serviceName); } } FrameworkEventCollector.getInstance().collectServiceStartEvent(startServiceArray.toString()); @@ -205,7 +207,7 @@ private static void addStopHook() { private static void offerEvent() { // Report service stop event - ArrayList stopServiceArray = new ArrayList<>(); + List stopServiceArray = new ArrayList<>(); for (BaseService baseService : new HashSet<>(SERVICES.values())) { stopServiceArray.add(baseService.getClass().getName()); } diff --git a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/utils/FileUtils.java b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/utils/FileUtils.java index a1ce45eed1..1f6fe8f55b 100644 --- a/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/utils/FileUtils.java +++ b/sermant-agentcore/sermant-agentcore-core/src/main/java/io/sermant/core/utils/FileUtils.java @@ -17,15 +17,23 @@ package io.sermant.core.utils; import io.sermant.core.common.LoggerFactory; +import io.sermant.core.config.ConfigManager; +import io.sermant.core.plugin.agent.config.AgentConfig; +import java.io.BufferedReader; +import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; import java.io.FilenameFilter; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; import java.util.logging.Logger; @@ -47,6 +55,16 @@ public class FileUtils { private static final String AGENT_PATH = new File(new File(FileUtils.class.getProtectionDomain().getCodeSource() .getLocation().getPath()).getParent()).getParent(); + /** + * file name of unmatched class name + */ + private static final String DEFAULT_OUTPUT_CLASS_NAME_FILE = "unmatched_class_name.txt"; + + /** + * unmatched class cache + */ + private static final Map UNMATCHED_CLASS_CACHE = new ConcurrentHashMap<>(); + /** * buffer size */ @@ -224,4 +242,58 @@ private static boolean matchFileByWildcard(String name, String[] wcs) { } return false; } + + /** + * add unmatched class for enhancement to cache + * + * @param className class name + */ + public static void addToUnmatchedClassCache(String className) { + UNMATCHED_CLASS_CACHE.putIfAbsent(className, className); + } + + /** + * output file + */ + public static void writeUnmatchedClassNameToFile() { + try (BufferedWriter classNameWriter = new BufferedWriter(new FileWriter(buildUnmatchedFileString()))) { + for (String className : UNMATCHED_CLASS_CACHE.keySet()) { + classNameWriter.write(className); + classNameWriter.newLine(); + } + } catch (IOException e) { + LOGGER.log(Level.WARNING, "Fail to write class name to file. ", e); + } + } + + /** + * load unmatched file name to cache + */ + public static void readUnmatchedClassNameFromFile() { + try (BufferedReader reader = new BufferedReader(new FileReader(buildUnmatchedFileString()))) { + String line; + while ((line = reader.readLine()) != null) { + UNMATCHED_CLASS_CACHE.putIfAbsent(line, line); + } + } catch (IOException e) { + LOGGER.log(Level.WARNING, "Fail to read class name from file: " + e.getMessage()); + } + } + + public static Map getUnMatchedClassCache() { + return UNMATCHED_CLASS_CACHE; + } + + private static String buildUnmatchedFileString() { + AgentConfig config = ConfigManager.getConfig(AgentConfig.class); + String preFilterPath = config.getPreFilterPath(); + if (StringUtils.isEmpty(preFilterPath)) { + preFilterPath = AGENT_PATH; + } + String preFilterFile = config.getPreFilterFile(); + if (StringUtils.isEmpty(preFilterFile)) { + preFilterFile = DEFAULT_OUTPUT_CLASS_NAME_FILE; + } + return preFilterPath + File.separatorChar + preFilterFile; + } } diff --git a/sermant-agentcore/sermant-agentcore-god/src/main/java/io/sermant/god/common/SermantClassLoader.java b/sermant-agentcore/sermant-agentcore-god/src/main/java/io/sermant/god/common/SermantClassLoader.java index 9416dd384a..0a6e8c64ec 100644 --- a/sermant-agentcore/sermant-agentcore-god/src/main/java/io/sermant/god/common/SermantClassLoader.java +++ b/sermant-agentcore/sermant-agentcore-god/src/main/java/io/sermant/god/common/SermantClassLoader.java @@ -22,6 +22,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -104,7 +105,7 @@ public void appendUrl(URL url) { * * @param urls urls */ - public void appendUrls(URL[] urls) { + public void appendUrls(List urls) { for (URL url : urls) { this.addURL(url); } diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/config/LoadPropertiesStrategy.java b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/config/LoadPropertiesStrategy.java index 56962f7aa3..d0c2e5f98f 100644 --- a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/config/LoadPropertiesStrategy.java +++ b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/config/LoadPropertiesStrategy.java @@ -69,6 +69,11 @@ public class LoadPropertiesStrategy implements LoadConfigStrategy { */ private static final int MAP_ARGUMENT_TYPE_LEN = 2; + /** + * collection pattern + */ + private static final String COLLECTION_PATTERN = "(.*)\\[[0-9]*]$"; + /** * argsMap */ @@ -141,21 +146,11 @@ private R loadConfig(Properties holder, Class cls, R c */ private Properties readConfig(File config) { final Properties properties = new Properties(); - Reader reader = null; - try { - reader = new InputStreamReader(new FileInputStream(config), CommonConstant.DEFAULT_CHARSET); + try (Reader reader = new InputStreamReader(new FileInputStream(config), CommonConstant.DEFAULT_CHARSET)) { properties.load(reader); } catch (IOException ignored) { LOGGER.log(Level.WARNING, String.format(Locale.ROOT, "Missing config file [%s], please check.", config)); - } finally { - if (reader != null) { - try { - reader.close(); - } catch (IOException ignored) { - LOGGER.warning("Unexpected exception occurs. "); - } - } } return properties; } @@ -216,7 +211,7 @@ public String readMapOrCollection(String configVal, Properties config, String ke for (String propertyName : config.stringPropertyNames()) { if (propertyName.startsWith(key)) { // Match List and Set - if (propertyName.matches("(.*)\\[[0-9]*]$")) { + if (propertyName.matches(COLLECTION_PATTERN)) { sb.append(config.getProperty(propertyName)) .append(CommonConstant.COMMA); } else { diff --git a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/config/LoadYamlStrategy.java b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/config/LoadYamlStrategy.java index 11663951b9..2e1e89b8af 100644 --- a/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/config/LoadYamlStrategy.java +++ b/sermant-agentcore/sermant-agentcore-implement/src/main/java/io/sermant/implement/config/LoadYamlStrategy.java @@ -16,8 +16,6 @@ package io.sermant.implement.config; -import com.alibaba.fastjson.util.IOUtils; - import io.sermant.core.common.CommonConstant; import io.sermant.core.common.LoggerFactory; import io.sermant.core.config.common.BaseConfig; @@ -105,8 +103,8 @@ public boolean canLoad(File file) { } @Override - public Map getConfigHolder(File config, Map bootstreapArgsMap) { - this.argsMap = bootstreapArgsMap; + public Map getConfigHolder(File config, Map bootstrapArgsMap) { + this.argsMap = bootstrapArgsMap; return readConfig(config); } @@ -142,16 +140,12 @@ public R loadConfig(Map holder, R config, boolean isDynam * @return Configuration information */ private Map readConfig(File config) { - BufferedReader reader = null; - try { - reader = new BufferedReader(new InputStreamReader(new FileInputStream(config), - CommonConstant.DEFAULT_CHARSET)); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(config), + CommonConstant.DEFAULT_CHARSET))) { return yaml.loadAs(reader, Map.class); } catch (IOException ignored) { LOGGER.log(Level.WARNING, String.format(Locale.ROOT, "Missing config file [%s], please check.", config)); - } finally { - IOUtils.close(reader); } return Collections.emptyMap(); } diff --git a/sermant-agentcore/sermant-agentcore-premain/src/main/java/io/sermant/premain/AgentLauncher.java b/sermant-agentcore/sermant-agentcore-premain/src/main/java/io/sermant/premain/AgentLauncher.java index 50a7d15fcf..3bf0d0862c 100644 --- a/sermant-agentcore/sermant-agentcore-premain/src/main/java/io/sermant/premain/AgentLauncher.java +++ b/sermant-agentcore/sermant-agentcore-premain/src/main/java/io/sermant/premain/AgentLauncher.java @@ -118,10 +118,11 @@ private static void installAgent(Instrumentation instrumentation, boolean isDyna } } catch (InvocationTargetException invocationTargetException) { LOGGER.log(Level.SEVERE, - "Install agent failed: " + invocationTargetException.getTargetException().getMessage()); + "Install agent failed: " + LoggerUtils.recordStackTrace( + invocationTargetException.getTargetException())); } catch (IOException | IllegalAccessException | NoSuchMethodException | ClassNotFoundException exception) { LOGGER.log(Level.SEVERE, - "Install agent failed: " + exception.getMessage()); + "Install agent failed: " + LoggerUtils.recordStackTrace(exception)); } } diff --git a/sermant-agentcore/sermant-agentcore-premain/src/main/java/io/sermant/premain/utils/LoggerUtils.java b/sermant-agentcore/sermant-agentcore-premain/src/main/java/io/sermant/premain/utils/LoggerUtils.java index b8ae3cbae3..78e4cc9a6d 100644 --- a/sermant-agentcore/sermant-agentcore-premain/src/main/java/io/sermant/premain/utils/LoggerUtils.java +++ b/sermant-agentcore/sermant-agentcore-premain/src/main/java/io/sermant/premain/utils/LoggerUtils.java @@ -17,6 +17,8 @@ package io.sermant.premain.utils; import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.Objects; import java.util.logging.ConsoleHandler; import java.util.logging.Formatter; import java.util.logging.LogRecord; @@ -58,4 +60,20 @@ public String format(LogRecord record) { public static Logger getLogger() { return LOGGER; } + + /** + * record stack trace + * + * @param throwable throwable + * @return record string + */ + public static String recordStackTrace(Throwable throwable) { + StringBuilder record = new StringBuilder( + throwable + System.lineSeparator()); + StackTraceElement[] messages = throwable.getStackTrace(); + Arrays.stream(messages).filter(Objects::nonNull).forEach(stackTraceElement -> { + record.append(stackTraceElement).append(System.lineSeparator()); + }); + return record.toString(); + } }