TtlAgent.java 7.1 KB
Newer Older
1
package com.alibaba.ttl.threadpool.agent;
2 3


oldratlee's avatar
oldratlee 已提交
4
import com.alibaba.ttl.threadpool.agent.internal.logging.Logger;
oldratlee's avatar
oldratlee 已提交
5
import com.alibaba.ttl.threadpool.agent.internal.transformlet.JavassistTransformlet;
6 7
import com.alibaba.ttl.threadpool.agent.internal.transformlet.impl.TtlExecutorTransformlet;
import com.alibaba.ttl.threadpool.agent.internal.transformlet.impl.TtlForkJoinTransformlet;
oldratlee's avatar
oldratlee 已提交
8
import com.alibaba.ttl.threadpool.agent.internal.transformlet.impl.TtlTimerTaskTransformlet;
9

oldratlee's avatar
oldratlee 已提交
10 11
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
12 13
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
14 15 16 17
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
18
import java.util.logging.Level;
19

20
/**
21
 * TTL Java Agent.
oldratlee's avatar
oldratlee 已提交
22 23 24
 * <p>
 * <b><i>NOTE:</i></b><br>
 * Since {@code v2.6.0}, TTL agent jar will auto add self to {@code boot classpath}.
oldratlee's avatar
oldratlee 已提交
25
 * But you <b>should <i>NOT</i></b> modify the downloaded TTL jar file name in the maven repo(eg: {@code transmittable-thread-local-2.x.x.jar}).<br>
oldratlee's avatar
oldratlee 已提交
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
 * 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
 * by java option {@code -Xbootclasspath/a:path/to/ttl-foo-name-changed.jar}.
 * <p>
 * The implementation of auto adding self agent jar to {@code boot classpath} use
 * the {@code Boot-Class-Path} property of manifest file({@code META-INF/MANIFEST.MF}) in the TTL Java Agent Jar:
 * <blockquote>
 * <dl>
 * <dt>Boot-Class-Path</dt>
 * <dd>
 * A list of paths to be searched by the bootstrap class loader. Paths represent directories or libraries (commonly referred to as JAR or zip libraries on many platforms).
 * These paths are searched by the bootstrap class loader after the platform specific mechanisms of locating a class have failed. Paths are searched in the order listed.
 * </dd>
 * </dl>
 * </blockquote>
 * <p>
 * More info about {@code Boot-Class-Path} see
oldratlee's avatar
oldratlee 已提交
43
 * <a href="https://docs.oracle.com/javase/10/docs/api/java/lang/instrument/package-summary.html">The mechanism for instrumentation</a>.
44
 *
oldratlee's avatar
oldratlee 已提交
45
 * @author Jerry Lee (oldratlee at gmail dot com)
oldratlee's avatar
oldratlee 已提交
46
 * @see Instrumentation
oldratlee's avatar
oldratlee 已提交
47 48
 * @see <a href="https://docs.oracle.com/javase/10/docs/api/java/lang/instrument/package-summary.html">The mechanism for instrumentation</a>
 * @see <a href="https://docs.oracle.com/javase/10/docs/technotes/guides/jar/jar.html#JAR_Manifest">JAR File Specification - JAR Manifest</a>
oldratlee's avatar
oldratlee 已提交
49
 * @see <a href="https://docs.oracle.com/javase/tutorial/deployment/jar/manifestindex.html">Working with Manifest Files - The Java™ TutorialsHide</a>
oldratlee's avatar
oldratlee 已提交
50
 * @since 0.9.0
51
 */
S
smisger 已提交
52 53
public final class TtlAgent {
    private TtlAgent() {
54
        throw new InstantiationError("Must not instantiate this class");
S
smisger 已提交
55
    }
56

oldratlee's avatar
oldratlee 已提交
57 58
    /**
     * Entrance method of TTL Java Agent.
oldratlee's avatar
oldratlee 已提交
59 60 61 62 63 64
     *
     * <h2>TTL Agent configuration</h2>
     * Configure TTL agent via agent arguments, format is {@code k1:v1,k2:v2}. Below is available configuration keys.
     *
     * <h3>The log configuration</h3>
     * The log of TTL Java Agent is config by key {@code ttl.agent.logger}.
oldratlee's avatar
oldratlee 已提交
65 66 67 68 69 70
     *
     * <ul>
     * <li>{@code ttl.agent.logger : STDERR}<br>
     * only log to {@code stderr} when error.
     * This is <b>default</b>, when no/unrecognized configuration for key {@code ttl.agent.logger}.</li>
     * <li>{@code ttl.agent.logger : STDOUT}<br>
oldratlee's avatar
oldratlee 已提交
71
     * Log to {@code stdout}, more info than {@code ttl.agent.logger:STDERR}; This is needed when developing.</li>
oldratlee's avatar
oldratlee 已提交
72 73 74 75
     * </ul>
     * <p>
     * configuration example:
     * <ul>
oldratlee's avatar
oldratlee 已提交
76 77
     * <li>{@code -javaagent:/path/to/transmittable-thread-local-2.x.x.jar}</li>
     * <li>{@code -javaagent:/path/to/transmittable-thread-local-2.x.x.jar=ttl.agent.logger:STDOUT}</li>
oldratlee's avatar
oldratlee 已提交
78 79
     * </ul>
     *
oldratlee's avatar
oldratlee 已提交
80
     * <h3>Enable TimerTask class decoration</h3>
81 82
     * Enable TimerTask class decoration is config by key {@code ttl.agent.enable.timer.task}.
     * When no configuration for this key, default does not enabled.
oldratlee's avatar
oldratlee 已提交
83
     * <p>
84 85
     * Configuration example:<br>
     * {@code -javaagent:/path/to/transmittable-thread-local-2.x.x.jar=ttl.agent.enable.timer.task:true}
oldratlee's avatar
oldratlee 已提交
86 87 88 89
     *
     * <h3>Multi key configuration example</h3>
     * {@code -javaagent:/path/to/transmittable-thread-local-2.x.x.jar=ttl.agent.logger:STDOUT,ttl.agent.enable.timer.task:true}
     *
oldratlee's avatar
oldratlee 已提交
90
     * @see <a href="https://docs.oracle.com/javase/10/docs/api/java/lang/instrument/package-summary.html">The mechanism for instrumentation</a>
oldratlee's avatar
oldratlee 已提交
91 92 93 94 95
     * @see Logger
     * @see Logger#TTL_AGENT_LOGGER_KEY
     * @see Logger#STDERR
     * @see Logger#STDOUT
     */
oldratlee's avatar
oldratlee 已提交
96
    public static void premain(String agentArgs, @Nonnull Instrumentation inst) {
oldratlee's avatar
oldratlee 已提交
97 98 99
        final Map<String, String> kvs = splitCommaColonStringToKV(agentArgs);

        Logger.setLoggerImplType(getLogImplTypeFromAgentArgs(kvs));
100
        final Logger logger = Logger.getLogger(TtlAgent.class);
101

102
        try {
103
            logger.info("[TtlAgent.premain] begin, agentArgs: " + agentArgs + ", Instrumentation: " + inst);
104

oldratlee's avatar
oldratlee 已提交
105 106 107 108 109 110 111 112
            final List<Class<? extends JavassistTransformlet>> transformletList = new ArrayList<Class<? extends JavassistTransformlet>>();
            transformletList.add(TtlExecutorTransformlet.class);
            transformletList.add(TtlForkJoinTransformlet.class);
            if (enableTimerTask(kvs)) {
                transformletList.add(TtlTimerTaskTransformlet.class);
            }

            final ClassFileTransformer transformer = new TtlTransformer(transformletList);
113 114
            inst.addTransformer(transformer, true);
            logger.info("[TtlAgent.premain] addTransformer " + transformer.getClass() + " success");
115

116 117 118
            logger.info("[TtlAgent.premain] end");
        } catch (Exception e) {
            String msg = "Fail to load TtlAgent , cause: " + e.toString();
119
            logger.log(Level.SEVERE, msg, e);
120 121 122
            throw new IllegalStateException(msg, e);
        }
    }
123

oldratlee's avatar
oldratlee 已提交
124
    private static String getLogImplTypeFromAgentArgs(@Nonnull final Map<String, String> kvs) {
oldratlee's avatar
oldratlee 已提交
125 126 127 128 129
        return kvs.get(Logger.TTL_AGENT_LOGGER_KEY);
    }

    private static final String TTL_AGENT_ENABLE_TIMER_TASK_KEY = "ttl.agent.enable.timer.task";

oldratlee's avatar
oldratlee 已提交
130
    private static boolean enableTimerTask(@Nonnull final Map<String, String> kvs) {
oldratlee's avatar
oldratlee 已提交
131 132 133 134
        final boolean hasEnableKey = kvs.containsKey(TTL_AGENT_ENABLE_TIMER_TASK_KEY);
        if (!hasEnableKey) return false;

        return !"false".equalsIgnoreCase(kvs.get(TTL_AGENT_ENABLE_TIMER_TASK_KEY));
135 136 137
    }

    /**
138
     * Split to {@code json} like String({@code "k1:v1,k2:v2"}) to KV map({@code "k1"->"v1", "k2"->"v2"}).
139
     */
oldratlee's avatar
oldratlee 已提交
140 141
    @Nonnull
    static Map<String, String> splitCommaColonStringToKV(@Nullable String commaColonString) {
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
        Map<String, String> ret = new HashMap<String, String>();
        if (commaColonString == null || commaColonString.trim().length() == 0) return ret;

        final String[] splitKvArray = commaColonString.trim().split("\\s*,\\s*");
        for (String kvString : splitKvArray) {
            final String[] kv = kvString.trim().split("\\s*:\\s*");
            if (kv.length == 0) continue;

            if (kv.length == 1) ret.put(kv[0], "");
            else {
                ret.put(kv[0], kv[1]);
            }
        }

        return ret;
    }
158
}