001/**
002 * Copyright (c) 2004-2011 QOS.ch
003 * All rights reserved.
004 *
005 * Permission is hereby granted, free  of charge, to any person obtaining
006 * a  copy  of this  software  and  associated  documentation files  (the
007 * "Software"), to  deal in  the Software without  restriction, including
008 * without limitation  the rights to  use, copy, modify,  merge, publish,
009 * distribute,  sublicense, and/or sell  copies of  the Software,  and to
010 * permit persons to whom the Software  is furnished to do so, subject to
011 * the following conditions:
012 *
013 * The  above  copyright  notice  and  this permission  notice  shall  be
014 * included in all copies or substantial portions of the Software.
015 *
016 * THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
017 * EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
018 * MERCHANTABILITY,    FITNESS    FOR    A   PARTICULAR    PURPOSE    AND
019 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
020 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
021 * OF CONTRACT, TORT OR OTHERWISE,  ARISING FROM, OUT OF OR IN CONNECTION
022 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
023 *
024 */
025package org.slf4j.agent;
026
027import java.io.ByteArrayInputStream;
028import java.io.IOException;
029import java.lang.instrument.Instrumentation;
030import java.util.Date;
031import java.util.Properties;
032
033import org.slf4j.instrumentation.LogTransformer;
034
035/**
036 * Entry point for slf4j-ext when used as a Java agent.
037 *
038 */
039public class AgentPremain {
040
041    /**
042     * JavaAgent premain entry point as specified in the MANIFEST.MF file. See
043     * <a href="http://java.sun.com/javase/6/docs/api/java/lang/instrument/package-summary.html">http://java.sun.com/javase/6/docs/api/java/lang/instrument/package-summary.html</a> for details.
044     *
045     * @param agentArgument
046     *            string provided after "=" up to first space
047     * @param instrumentation
048     *            instrumentation environment provided by the JVM
049     */
050    public static void premain(String agentArgument, Instrumentation instrumentation) {
051
052        // We cannot do sanity checks for slf4j here as the jars loaded
053        // by the application are not visible here.
054
055        LogTransformer.Builder builder = new LogTransformer.Builder();
056        builder = builder.addEntryExit(true);
057
058        if (agentArgument != null) {
059            Properties args = parseArguments(agentArgument, ",");
060
061            if (args.containsKey(AgentOptions.VERBOSE)) {
062                builder = builder.verbose(true);
063            }
064
065            if (args.containsKey(AgentOptions.TIME)) {
066                printStartStopTimes();
067            }
068
069            if (args.containsKey(AgentOptions.IGNORE)) {
070                String ignore = args.getProperty(AgentOptions.IGNORE);
071                builder = builder.ignore(ignore.split(":"));
072            }
073
074            if (args.containsKey(AgentOptions.LEVEL)) {
075                builder = builder.level(args.getProperty(AgentOptions.LEVEL));
076            }
077        }
078
079        instrumentation.addTransformer(builder.build());
080    }
081
082    /**
083     * Consider the argument string to be a property file (by converting the
084     * splitter character to line feeds), and then reading it like any other
085     * property file.
086     *
087     *
088     * @param agentArgument
089     *            string given by instrumentation framework
090     * @param separator
091     *            String to convert to line feeds
092     * @return argument converted to properties
093     */
094    private static Properties parseArguments(String agentArgument, String separator) {
095        Properties p = new Properties();
096        try {
097            String argumentAsLines = agentArgument.replaceAll(separator, "\n");
098            p.load(new ByteArrayInputStream(argumentAsLines.getBytes()));
099        } catch (IOException e) {
100            String s = "Could not load arguments as properties";
101            throw new RuntimeException(s, e);
102        }
103        return p;
104    }
105
106    /**
107     * Print the start message to System.err with the time NOW, and register a
108     * shutdown hook which will print the stop message to System.err with the
109     * time then and the number of milliseconds passed since.
110     *
111     */
112    private static void printStartStopTimes() {
113        final long start = System.currentTimeMillis();
114
115        System.err.println("Start at " + new Date());
116
117        Thread hook = new Thread(() -> {
118            long timePassed = System.currentTimeMillis() - start;
119            System.err.println("Stop at " + new Date() + ", execution time = " + timePassed + " ms");
120        });
121        Runtime.getRuntime().addShutdownHook(hook);
122    }
123}