001/*
002 * Copyright (C) 2015-present The Prometheus jmx_exporter Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package io.prometheus.jmx;
018
019import io.prometheus.jmx.common.http.ConfigurationException;
020import io.prometheus.jmx.common.http.HTTPServerFactory;
021import io.prometheus.metrics.exporter.httpserver.HTTPServer;
022import io.prometheus.metrics.instrumentation.jvm.JvmMetrics;
023import io.prometheus.metrics.model.registry.PrometheusRegistry;
024import java.io.File;
025import java.lang.instrument.Instrumentation;
026import java.net.InetAddress;
027import java.util.regex.Matcher;
028import java.util.regex.Pattern;
029
030public class JavaAgent {
031
032    public static final String CONFIGURATION_REGEX =
033            "^(?:((?:[\\w.-]+)|(?:\\[.+])):)?"
034                    + // host name, or ipv4, or ipv6 address in brackets
035                    "(\\d{1,5}):"
036                    + // port
037                    "(.+)"; // config file
038
039    private static final String DEFAULT_HOST = "0.0.0.0";
040
041    private static HTTPServer httpServer;
042
043    public static void agentmain(String agentArgument, Instrumentation instrumentation)
044            throws Exception {
045        premain(agentArgument, instrumentation);
046    }
047
048    public static void premain(String agentArgument, Instrumentation instrumentation)
049            throws Exception {
050        try {
051            Config config = parseConfig(agentArgument);
052
053            new BuildInfoMetrics().register(PrometheusRegistry.defaultRegistry);
054            JvmMetrics.builder().register(PrometheusRegistry.defaultRegistry);
055            new JmxCollector(new File(config.file), JmxCollector.Mode.AGENT)
056                    .register(PrometheusRegistry.defaultRegistry);
057
058            String host = config.host != null ? config.host : DEFAULT_HOST;
059
060            httpServer =
061                    new HTTPServerFactory()
062                            .createHTTPServer(
063                                    InetAddress.getByName(host),
064                                    config.port,
065                                    PrometheusRegistry.defaultRegistry,
066                                    new File(config.file));
067        } catch (Throwable t) {
068            synchronized (System.err) {
069                System.err.println("Failed to start Prometheus JMX Exporter");
070                System.err.println();
071                t.printStackTrace();
072                System.err.println();
073                System.err.println("Prometheus JMX Exporter exiting");
074                System.err.flush();
075            }
076            System.exit(1);
077        }
078    }
079
080    /**
081     * Parse the Java Agent configuration. The arguments are typically specified to the JVM as a
082     * javaagent as {@code -javaagent:/path/to/agent.jar=<CONFIG>}. This method parses the {@code
083     * <CONFIG>} portion.
084     *
085     * @param args provided agent args
086     * @return configuration to use for our application
087     */
088    private static Config parseConfig(String args) {
089        Pattern pattern = Pattern.compile(CONFIGURATION_REGEX);
090
091        Matcher matcher = pattern.matcher(args);
092        if (!matcher.matches()) {
093            System.err.println(
094                    "Usage: -javaagent:/path/to/JavaAgent.jar=[host:]<port>:<yaml configuration"
095                            + " file> ");
096            throw new ConfigurationException("Malformed arguments - " + args);
097        }
098
099        String givenHost = matcher.group(1);
100        String givenPort = matcher.group(2);
101        String givenConfigFile = matcher.group(3);
102
103        int port = Integer.parseInt(givenPort);
104
105        return new Config(givenHost, port, givenConfigFile);
106    }
107
108    private static class Config {
109
110        String host;
111        int port;
112        String file;
113
114        Config(String host, int port, String file) {
115            this.host = host;
116            this.port = port;
117            this.file = file;
118        }
119    }
120}