1   package org.slf4j.agent;
2   
3   import static org.slf4j.helpers.MessageFormatter.format;
4   
5   import java.io.ByteArrayInputStream;
6   import java.io.IOException;
7   import java.lang.instrument.Instrumentation;
8   import java.util.Date;
9   import java.util.Properties;
10  
11  import org.slf4j.instrumentation.LogTransformer;
12  
13  /**
14   * Entry point for slf4j-ext when used as a Java agent.
15   * 
16   */
17  public class AgentPremain {
18  
19    private static final String START_MSG = "Start at {}";
20    private static final String STOP_MSG = "Stop at {}, execution time = {} ms";
21  
22    /**
23     * JavaAgent premain entry point as specified in the MANIFEST.MF file. See
24     * {@link http://java.sun.com/javase/6/docs/api/java/lang/instrument/package-summary.html}
25     *  for details.
26     * 
27     * @param agentArgument
28     *          string provided after "=" up to first space
29     * @param instrumentation
30     *          instrumentation environment provided by the JVM
31     */
32    public static void premain(String agentArgument,
33        Instrumentation instrumentation) {
34  
35      LogTransformer.Builder builder = new LogTransformer.Builder();
36      builder = builder.addEntryExit(true);
37  
38      if (agentArgument != null) {
39        Properties args = parseArguments(agentArgument, ",");
40  
41        if (args.containsKey(AgentOptions.VERBOSE)) {
42          builder = builder.verbose(true);
43        }
44  
45        if (args.containsKey(AgentOptions.TIME)) {
46          printStartStopTimes();
47        }
48  
49        if (args.containsKey(AgentOptions.IGNORE)) {
50          String ignore = args.getProperty(AgentOptions.IGNORE);
51          builder = builder.ignore(ignore.split(":"));
52        }
53  
54        if (args.containsKey(AgentOptions.LEVEL)) {
55          builder = builder.level(args.getProperty(AgentOptions.LEVEL));
56        }
57      }
58  
59      instrumentation.addTransformer(builder.build());
60    }
61  
62    /**
63     * Consider the argument string to be a property file (by converting the
64     * splitter character to line feeds), and then reading it like any other
65     * property file.
66     * 
67     * 
68     * @param agentArgument
69     *          string given by instrumentation framework
70     * @param separator
71     *          String to convert to line feeds
72     * @return argument converted to properties
73     */
74    private static Properties parseArguments(String agentArgument,
75        String separator) {
76      Properties p = new Properties();
77      try {
78        String argumentAsLines = agentArgument.replaceAll(separator, "\n");
79        p.load(new ByteArrayInputStream(argumentAsLines.getBytes()));
80      } catch (IOException e) {
81        String s = "Could not load arguments as properties";
82        throw new RuntimeException(s, e);
83      }
84      return p;
85    }
86  
87    /**
88     * Print the start message to System.err with the time NOW, and register a
89     * shutdown hook which will print the stop message to System.err with the time
90     * then and the number of milliseconds passed since.
91     * 
92     */
93    private static void printStartStopTimes() {
94      final long start = System.currentTimeMillis();
95      System.err.println(format(START_MSG, new Date()));
96  
97      Thread hook = new Thread() {
98        @Override
99        public void run() {
100         long timePassed = System.currentTimeMillis() - start;
101         String message = format(STOP_MSG, new Date(), timePassed);
102         System.err.println(message);
103       }
104     };
105     Runtime.getRuntime().addShutdownHook(hook);
106   }
107 }