001package io.prometheus.jmx;
002
003import java.io.File;
004import java.lang.instrument.Instrumentation;
005import java.net.InetSocketAddress;
006import java.util.regex.Matcher;
007import java.util.regex.Pattern;
008
009import io.prometheus.client.CollectorRegistry;
010import io.prometheus.client.exporter.HTTPServer;
011import io.prometheus.client.hotspot.DefaultExports;
012
013public class JavaAgent {
014
015    static HTTPServer server;
016
017    public static void agentmain(String agentArgument, Instrumentation instrumentation) throws Exception {
018        premain(agentArgument, instrumentation);
019    }
020
021    public static void premain(String agentArgument, Instrumentation instrumentation) throws Exception {
022        // Bind to all interfaces by default (this includes IPv6).
023        String host = "0.0.0.0";
024
025        try {
026            Config config = parseConfig(agentArgument, host);
027
028            new BuildInfoCollector().register();
029            new JmxCollector(new File(config.file)).register();
030            DefaultExports.initialize();
031            server = new HTTPServer(config.socket, CollectorRegistry.defaultRegistry, true);
032        }
033        catch (IllegalArgumentException e) {
034            System.err.println("Usage: -javaagent:/path/to/JavaAgent.jar=[host:]<port>:<yaml configuration file>");
035            System.exit(1);
036        }
037    }
038
039    /**
040     * Parse the Java Agent configuration. The arguments are typically specified to the JVM as a javaagent as
041     * {@code -javaagent:/path/to/agent.jar=<CONFIG>}. This method parses the {@code <CONFIG>} portion.
042     * @param args provided agent args
043     * @param ifc default bind interface
044     * @return configuration to use for our application
045     */
046    public static Config parseConfig(String args, String ifc) {
047        Pattern pattern = Pattern.compile(
048                "^(?:((?:[\\w.]+)|(?:\\[.+])):)?" +  // host name, or ipv4, or ipv6 address in brackets
049                        "(\\d{1,5}):" +              // port
050                        "(.+)");                     // config file
051
052        Matcher matcher = pattern.matcher(args);
053        if (!matcher.matches()) {
054            throw new IllegalArgumentException("Malformed arguments - " + args);
055        }
056
057        String givenHost = matcher.group(1);
058        String givenPort = matcher.group(2);
059        String givenConfigFile = matcher.group(3);
060
061        int port = Integer.parseInt(givenPort);
062
063        InetSocketAddress socket;
064        if (givenHost != null && !givenHost.isEmpty()) {
065            socket = new InetSocketAddress(givenHost, port);
066        }
067        else {
068            socket = new InetSocketAddress(ifc, port);
069            givenHost = ifc;
070        }
071
072        return new Config(givenHost, port, givenConfigFile, socket);
073    }
074
075    static class Config {
076        String host;
077        int port;
078        String file;
079        InetSocketAddress socket;
080
081        Config(String host, int port, String file, InetSocketAddress socket) {
082            this.host = host;
083            this.port = port;
084            this.file = file;
085            this.socket = socket;
086        }
087    }
088}