diff --git a/src/main/java/com/alibaba/ttl/threadpool/agent/TtlAgent.java b/src/main/java/com/alibaba/ttl/threadpool/agent/TtlAgent.java index 825f04f2af845545a0b565d149ed2ceac00de734..289e84a6439297771ea2b03edbd55bec961083fb 100644 --- a/src/main/java/com/alibaba/ttl/threadpool/agent/TtlAgent.java +++ b/src/main/java/com/alibaba/ttl/threadpool/agent/TtlAgent.java @@ -16,14 +16,44 @@ import java.util.Map; /** * TTL Java Agent. * - *

The configuration/arguments for TTL agent

+ *

The configuration for TTL agent

*

- * Configure TTL agent via agent arguments, format is {@code k1:v1,k2:v2}. Below is available configuration keys. + * Configure TTL agent via {@code -D property}({@link System#getProperties()}) or TTL agent arguments. + *

    + *
  1. {@code -D property} config format is: {@code -Dkey1=v2 -Dkey2=v2}
  2. + *
  3. TTL agent arguments config format is {@code key1:v1,key2:v2}.
    + * separate key-value pairs by {@code char ,}, and separate key-value by {@code char :}.
  4. + *
+ * NOTE about the config sources and the precedence:
+ *
    + *
  1. Read {@code -D property}({@link System#getProperties()}) first.
  2. + *
  3. if no {@code -D property} configured(including empty property value configured by {@code -Dkey1}/{@code -Dkey1=}), read TTL Agent argument configuration.
  4. + *
+ * Below is available TTL agent configuration keys. * - *

Disable inheritable for thread pool

+ *

Configuration key: Log Type

+ *

+ * The log of TTL Java Agent is configured by key {@code ttl.agent.logger}. Since version {@code 2.6.0}. + * + *

+ *

+ * Configuration example: + * + *

    + *
  1. {@code -Dttl.agent.logger=STDOUT}
  2. + *
  3. {@code -javaagent:/path/to/transmittable-thread-local-2.x.y.jar=ttl.agent.logger:STDOUT}
  4. + *
+ * + *

Configuration key: Disable inheritable for thread pool

*

* Enable "disable inheritable" for thread pool, config by key {@code ttl.agent.disable.inheritable.for.thread.pool}. - * When no configuration for this key, default does not enabled. Since version {@code 2.10.1}. + * When no configuration for this key, default is {@code false}(aka. dose not disable inheritable). Since version {@code 2.10.1}. * *

* More info about "disable inheritable" see {@link com.alibaba.ttl.TransmittableThreadLocal}. *

- * Configuration example:
- * {@code -javaagent:/path/to/transmittable-thread-local-2.x.y.jar=ttl.agent.disable.inheritable.for.thread.pool:true} + * Configuration example: * - *

The log configuration

- * The log of TTL Java Agent is config by key {@code ttl.agent.logger}. Since version {@code 2.6.0}. + *
    + *
  1. {@code -Dttl.agent.disable.inheritable.for.thread.pool=true}
  2. + *
  3. {@code -javaagent:/path/to/transmittable-thread-local-2.x.y.jar=ttl.agent.disable.inheritable.for.thread.pool:true}
  4. + *
* - * + *

Configuration key: Enable/Disable TimerTask class decoration

*

- * configuration example: - *

- * - *

Enable/disable TimerTask class decoration

* Enable/disable TimerTask class decoration is config by key {@code ttl.agent.enable.timer.task}. * Since version {@code 2.7.0}. *

@@ -68,16 +86,23 @@ import java.util.Map; * Before version {@code 2.11.1} default value is {@code false}. *

* Configuration example: - *

+ * * *

Multi key configuration example

+ *

+ * For TTL agent arguments config, example:
* {@code -javaagent:/path/to/transmittable-thread-local-2.x.y.jar=ttl.agent.logger:STDOUT,ttl.agent.disable.inheritable.for.thread.pool:true} + *

+ * For {@code -D property} config, simply specify multiply {@code -D property}, example:
+ * {@code -Dttl.agent.logger=STDOUT -Dttl.agent.disable.inheritable.for.thread.pool=true} * *

About boot classpath for TTL agent

- * NOTE: Since {@code v2.6.0}, TTL agent jar will auto add self to {@code boot classpath}. + *

+ * NOTE: Since {@code v2.6.0}, TTL agent jar will auto add self to {@code boot classpath}.
* But you should NOT modify the downloaded TTL jar file name in the maven repo(eg: {@code transmittable-thread-local-2.x.y.jar}).
* if you modified the downloaded TTL agent jar file name(eg: {@code ttl-foo-name-changed.jar}), * you must add TTL agent jar to {@code boot classpath} manually @@ -111,18 +136,28 @@ import java.util.Map; * @since 0.9.0 */ public final class TtlAgent { + + private static final String TTL_AGENT_LOGGER_KEY = "ttl.agent.logger"; + + private static final String TTL_AGENT_LOG_CLASS_TRANSFORM_KEY = "ttl.agent.log.class.transform"; + + private static final String TTL_AGENT_DISABLE_INHERITABLE_FOR_THREAD_POOL_KEY = "ttl.agent.disable.inheritable.for.thread.pool"; + + private static final String TTL_AGENT_ENABLE_TIMER_TASK_KEY = "ttl.agent.enable.timer.task"; + + private static volatile Map kvs; + + private static volatile boolean ttlAgentLoaded = false; + /** * Entrance method of TTL Java Agent. * - * @see Logger - * @see Logger#TTL_AGENT_LOGGER_KEY - * @see Logger#STDERR - * @see Logger#STDOUT + * @see TtlAgent */ public static void premain(final String agentArgs, @NonNull final Instrumentation inst) { kvs = TtlAgentHelper.splitCommaColonStringToKV(agentArgs); - Logger.setLoggerImplType(getLogImplTypeFromAgentArgs(kvs)); + Logger.setLoggerImplType(getLoggerType()); final Logger logger = Logger.getLogger(TtlAgent.class); try { @@ -133,8 +168,7 @@ public final class TtlAgent { transformletList.add(new ForkJoinTtlTransformlet()); if (isEnableTimerTask()) transformletList.add(new TimerTaskTtlTransformlet()); - final boolean logClassTransform = isBooleanOptionSet(TTL_AGENT_LOG_CLASS_TRANSFORM_KEY, false); - final ClassFileTransformer transformer = new TtlTransformer(transformletList, logClassTransform); + final ClassFileTransformer transformer = new TtlTransformer(transformletList, isLogClassTransform()); inst.addTransformer(transformer, true); logger.info("[TtlAgent.premain] add Transformer " + transformer.getClass().getName() + " success"); @@ -148,14 +182,6 @@ public final class TtlAgent { } } - private static volatile Map kvs; - - private static volatile boolean ttlAgentLoaded = false; - - private static String getLogImplTypeFromAgentArgs(@NonNull final Map kvs) { - return kvs.get(Logger.TTL_AGENT_LOGGER_KEY); - } - /** * Whether TTL agent is loaded. * @@ -165,14 +191,10 @@ public final class TtlAgent { return ttlAgentLoaded; } - private static final String TTL_AGENT_LOG_CLASS_TRANSFORM_KEY = "ttl.agent.log.class.transform"; - - private static final String TTL_AGENT_DISABLE_INHERITABLE_FOR_THREAD_POOL_KEY = "ttl.agent.disable.inheritable.for.thread.pool"; - - private static final String TTL_AGENT_ENABLE_TIMER_TASK_KEY = "ttl.agent.enable.timer.task"; - /** * Whether disable inheritable for thread pool is enhanced by ttl agent, check {@link #isTtlAgentLoaded()} first. + *

+ * Same as {@code isBooleanOptionSet(TTL_AGENT_DISABLE_INHERITABLE_FOR_THREAD_POOL_KEY)}. * * @see com.alibaba.ttl.threadpool.TtlExecutors#getDefaultDisableInheritableThreadFactory() * @see com.alibaba.ttl.threadpool.TtlExecutors#getDisableInheritableThreadFactory(java.util.concurrent.ThreadFactory) @@ -180,19 +202,24 @@ public final class TtlAgent { * @see com.alibaba.ttl.threadpool.TtlForkJoinPoolHelper#getDefaultDisableInheritableForkJoinWorkerThreadFactory() * @see com.alibaba.ttl.threadpool.TtlForkJoinPoolHelper#getDisableInheritableForkJoinWorkerThreadFactory(java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory) * @see com.alibaba.ttl.threadpool.DisableInheritableForkJoinWorkerThreadFactory + * @see com.alibaba.ttl.TransmittableThreadLocal + * @see #isBooleanOptionSet(String) + * @see TtlAgent * @since 2.10.1 */ public static boolean isDisableInheritableForThreadPool() { - return isBooleanOptionSet(TTL_AGENT_DISABLE_INHERITABLE_FOR_THREAD_POOL_KEY, false); + return isBooleanOptionSet(TTL_AGENT_DISABLE_INHERITABLE_FOR_THREAD_POOL_KEY); } /** * Whether timer task is enhanced by ttl agent, check {@link #isTtlAgentLoaded()} first. *

- * Same as {@code isBooleanOptionSet(TTL_AGENT_ENABLE_TIMER_TASK_KEY, true)} + * Same as {@code isBooleanOptionSet(TTL_AGENT_ENABLE_TIMER_TASK_KEY, true)}. * * @see java.util.Timer * @see java.util.TimerTask + * @see #isBooleanOptionSet(String, boolean) + * @see TtlAgent * @since 2.10.1 */ public static boolean isEnableTimerTask() { @@ -200,38 +227,92 @@ public final class TtlAgent { } /** + * Whether logging the class transform when the class received by {@link TtlAgent}. + *

+ * Same as {@code isBooleanOptionSet(TTL_AGENT_LOG_CLASS_TRANSFORM_KEY)}. + * + * @see #isBooleanOptionSet(String) + * @see TtlAgent * @since 2.13.0 */ - public static boolean isBooleanOptionSet(@NonNull String key, boolean defaultValue) { - return TtlAgentHelper.isBooleanOptionSet(kvs, key, defaultValue); + public static boolean isLogClassTransform() { + return isBooleanOptionSet(TTL_AGENT_LOG_CLASS_TRANSFORM_KEY); } /** - * Get option value in TTL Agent configuration. + * Get the TTL Agent Log type. + *

+ * Same as {@code getStringOptionValue(TTL_AGENT_LOGGER_KEY, Logger.STDERR)}. + * + * @see Logger + * @see Logger#STDERR + * @see Logger#STDOUT + * @see #getStringOptionValue(String, String) + * @see TtlAgent + * @since 2.13.0 + */ + @NonNull + public static String getLoggerType() { + return getStringOptionValue(TTL_AGENT_LOGGER_KEY, Logger.STDERR); + } + + // ======== Generic Option Getters ======== + + /** + * Generic Option Getters for {@code boolean type} option. + * + * @see TtlAgent + * @since 2.13.0 + */ + public static boolean isBooleanOptionSet(@NonNull String key) { + return isBooleanOptionSet(key, false); + } + + /** + * Generic Option Getters for {@code boolean type} option. + * + * @see TtlAgent + * @since 2.13.0 + */ + public static boolean isBooleanOptionSet(@NonNull String key, boolean defaultValueIfKeyAbsent) { + return TtlAgentHelper.isBooleanOptionSet(kvs, key, defaultValueIfKeyAbsent); + } + + /** + * Generic Option Getters for {@code string type} option. *

* usage example: *

- * if TTL Agent configuration is {@code -javaagent:/path/to/transmittable-thread-local-2.x.y.jar=ttl.agent.logger:STDOUT}, + * if {@code -Dttl.agent.logger=STDOUT} or + * TTL Agent configuration is {@code -javaagent:/path/to/transmittable-thread-local-2.x.y.jar=ttl.agent.logger:STDOUT}, * {@code getOptionValue("ttl.agent.logger")} return {@code STDOUT}. * + * @see TtlAgent * @since 2.13.0 */ - public static String getOptionValue(@NonNull String key) { - return kvs.get(key); + @NonNull + public static String getStringOptionValue(@NonNull String key, @NonNull String defaultValue) { + return TtlAgentHelper.getStringOptionValue(kvs, key, defaultValue); } /** - * Get list string values of specified option in TTL Agent configuration. + * Generic Option Getters for {@code string list type} option. + *

+ * TTL configuration use {@code |} to separate items. *

- * TTL Agent configuration use {@code |} to separate items. - * if TTL Agent configuration is {@code -javaagent:/path/to/transmittable-thread-local-2.x.y.jar=foo.list:v1|v2|v3}, + * usage example:
+ * if {@code -Dfoo.list=v1|v2|v3} or + * TTL Agent configuration is {@code -javaagent:/path/to/transmittable-thread-local-2.x.y.jar=foo.list:v1|v2|v3}, * {@code getOptionValue("foo.list")} return {@code [v1, v2, v3]}. + * + * @see TtlAgent */ @NonNull static List getOptionStringListValues(@NonNull String key) { return TtlAgentHelper.getOptionStringListValues(kvs, key); } + private TtlAgent() { throw new InstantiationError("Must not instantiate this class"); } diff --git a/src/main/java/com/alibaba/ttl/threadpool/agent/TtlAgentHelper.java b/src/main/java/com/alibaba/ttl/threadpool/agent/TtlAgentHelper.java index 50aaf84077dcd0fdd02c48ceb482b5f8d6d3fdd0..7568459ba9d126f72bd86d3885e309cb02b431e1 100644 --- a/src/main/java/com/alibaba/ttl/threadpool/agent/TtlAgentHelper.java +++ b/src/main/java/com/alibaba/ttl/threadpool/agent/TtlAgentHelper.java @@ -10,23 +10,84 @@ import java.util.*; * @since 2.13.0 */ final class TtlAgentHelper { - static boolean isBooleanOptionSet(@Nullable final Map kvs, @NonNull String key, boolean defaultValue) { - if (null == kvs) return defaultValue; - final boolean containsKey = kvs.containsKey(key); - if (!containsKey) return defaultValue; + // ======== Option Getter Methods ======== - return !"false".equalsIgnoreCase(kvs.get(key)); + static boolean isBooleanOptionSet( + @Nullable final Map kvs, @NonNull String key, + boolean defaultValueIfKeyAbsent + ) { + return isBooleanOptionSet(kvs, key, defaultValueIfKeyAbsent, true); } + static boolean isBooleanOptionSet( + @Nullable final Map kvs, @NonNull String key, + boolean defaultValueIfKeyAbsent, boolean defaultValueIfValueAbsent + ) { + final String value; + + final Properties properties = System.getProperties(); + if (properties.containsKey(key)) { + value = properties.getProperty(key).trim(); + } else { + if (kvs == null) return defaultValueIfKeyAbsent; + + final boolean containsKey = kvs.containsKey(key); + if (!containsKey) return defaultValueIfKeyAbsent; + + value = kvs.get(key).trim(); + } + + // if value is blank + if (value.isEmpty()) return defaultValueIfValueAbsent; + + return !"false".equalsIgnoreCase(value); + } + + @NonNull + static String getStringOptionValue( + @Nullable final Map kvs, @NonNull String key, + @NonNull String defaultValue + ) { + final String value; + + final Properties properties = System.getProperties(); + if (properties.containsKey(key)) { + value = properties.getProperty(key).trim(); + } else { + if (kvs == null) return defaultValue; + + final boolean containsKey = kvs.containsKey(key); + if (!containsKey) return defaultValue; + + value = kvs.get(key).trim(); + } + + // if value is blank + if (value.isEmpty()) return defaultValue; + + return value; + } + + @NonNull @SuppressWarnings("unchecked") static List getOptionStringListValues(@Nullable final Map kvs, @NonNull String key) { - if (null == kvs) return Collections.EMPTY_LIST; + final String value; + + final Properties properties = System.getProperties(); + if (properties.containsKey(key)) { + value = properties.getProperty(key); + } else { + if (kvs == null) return Collections.EMPTY_LIST; + + value = kvs.get(key); + } - final String value = kvs.get(key); return splitListStringToStringList(value); } + // ======== Simple Parse Util Methods ======== + /** * Split {@code json} like String({@code "k1:v1,k2:v2"}) to KV map({@code "k1"->"v1", "k2"->"v2"}). */ @@ -50,6 +111,7 @@ final class TtlAgentHelper { /** * Split String {@code "v1|v2|v3"} to String List({@code [v1, v2, v3]}). */ + @NonNull static List splitListStringToStringList(@Nullable String listString) { final List ret = new ArrayList(); if (listString == null || listString.trim().length() == 0) return ret; @@ -64,6 +126,7 @@ final class TtlAgentHelper { return ret; } + private TtlAgentHelper() { throw new InstantiationError("Must not instantiate this class"); } diff --git a/src/main/java/com/alibaba/ttl/threadpool/agent/logging/Logger.java b/src/main/java/com/alibaba/ttl/threadpool/agent/logging/Logger.java index e4775ff4f652aec29e0c9850290ac148991f246b..7167ec0bc34d01ce23cfdc50b9e0edee13f6c4b1 100644 --- a/src/main/java/com/alibaba/ttl/threadpool/agent/logging/Logger.java +++ b/src/main/java/com/alibaba/ttl/threadpool/agent/logging/Logger.java @@ -13,7 +13,6 @@ import java.util.logging.Level; * @since 2.6.0 */ public abstract class Logger { - public static final String TTL_AGENT_LOGGER_KEY = "ttl.agent.logger"; public static final String STDOUT = "STDOUT"; public static final String STDERR = "STDERR";