com.googlecode.jmxtrans.model.output.LibratoWriter.java Source code

Java tutorial

Introduction

Here is the source code for com.googlecode.jmxtrans.model.output.LibratoWriter.java

Source

/**
 * The MIT License
 * Copyright  2010 JmxTrans team
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package com.googlecode.jmxtrans.model.output;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.Base64Variants;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Charsets;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
import com.googlecode.jmxtrans.model.Query;
import com.googlecode.jmxtrans.model.Result;
import com.googlecode.jmxtrans.model.Server;
import com.googlecode.jmxtrans.model.ValidationException;
import com.googlecode.jmxtrans.model.naming.KeyUtils;
import com.googlecode.jmxtrans.model.naming.StringUtils;
import lombok.EqualsAndHashCode;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.NullOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.URL;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.googlecode.jmxtrans.util.NumberUtils.isNumeric;

/**
 * This writer is a port of the LibratoWriter from the embedded-jmxtrans
 * project.
 * <p/>
 * <a href="https://metrics.librato.com//">Librato Metrics</a>
 * <p/>
 * This implementation uses <a href="http://dev.librato.com/v1/post/metrics">
 * POST {@code /v1/metrics}</a> HTTP API.
 * <p/>
 * Settings:
 * <ul>
 * <li>"{@code url}": Librato server URL. Optional, default value:
 * {@value #DEFAULT_LIBRATO_API_URL}.</li>
 * <li>"{@code username}": Librato username. Mandatory</li>
 * <li>"{@code token}": Librato token. Mandatory</li>
 * <li>"{@code libratoApiTimeoutInMillis}": read timeout of the calls to Librato
 * HTTP API. Optional, default value: 1000.</li>
 * </ul>
 *
 * @author <a href="mailto:cleclerc@cloudbees.com">Cyrille Le Clerc</a>
 */
@EqualsAndHashCode(exclude = { "jsonFactory" })
public class LibratoWriter extends BaseOutputWriter {

    public static final String SETTING_URL = "url";
    public static final String SETTING_USERNAME = "username";
    public static final String SETTING_TOKEN = "token";
    public static final String SETTING_PROXY_HOST = "proxyHost";
    public static final String SETTING_PROXY_PORT = "proxyPort";
    public static final String DEFAULT_LIBRATO_API_URL = "https://metrics-api.librato.com/v1/metrics";
    public static final String SETTING_LIBRATO_API_TIMEOUT_IN_MILLIS = "libratoApiTimeoutInMillis";

    private static final Logger logger = LoggerFactory.getLogger(LibratoWriter.class);

    private final JsonFactory jsonFactory = new JsonFactory();
    /**
     * Librato HTTP API URL
     */
    private final URL url;
    private final int libratoApiTimeoutInMillis;
    /**
     * Librato HTTP API authentication username
     */
    private final String username;
    private final String token;
    private final String basicAuthentication;
    /**
     * Optional proxy for the http API calls
     */
    private final String proxyHost;
    private final Integer proxyPort;
    private Proxy proxy;

    @VisibleForTesting
    final String httpUserAgent;

    @JsonCreator
    public LibratoWriter(@JsonProperty("typeNames") ImmutableList<String> typeNames,
            @JsonProperty("booleanAsNumber") boolean booleanAsNumber, @JsonProperty("debug") Boolean debugEnabled,
            @JsonProperty("url") URL url,
            @JsonProperty("libratoApiTimeoutInMillis") Integer libratoApiTimeoutInMillis,
            @JsonProperty("username") String username, @JsonProperty("token") String token,
            @JsonProperty("proxyHost") String proxyHost, @JsonProperty("proxyPort") Integer proxyPort,
            @JsonProperty("settings") Map<String, Object> settings) throws MalformedURLException {
        super(typeNames, booleanAsNumber, debugEnabled, settings);
        logger.warn("LibratoWriter is deprecated. Please use LibratoWriterFactory instead.");
        this.url = MoreObjects.firstNonNull(url, new URL(
                MoreObjects.firstNonNull((String) this.getSettings().get(SETTING_URL), DEFAULT_LIBRATO_API_URL)));
        this.libratoApiTimeoutInMillis = MoreObjects.firstNonNull(libratoApiTimeoutInMillis,
                Settings.getIntSetting(getSettings(), SETTING_LIBRATO_API_TIMEOUT_IN_MILLIS, 1000));
        this.username = MoreObjects.firstNonNull(username, (String) getSettings().get(SETTING_USERNAME));
        this.token = MoreObjects.firstNonNull(token, (String) getSettings().get(SETTING_TOKEN));
        this.basicAuthentication = Base64Variants.getDefaultVariant()
                .encode((this.username + ":" + this.token).getBytes(Charsets.US_ASCII));
        this.proxyHost = proxyHost != null ? proxyHost : (String) getSettings().get(SETTING_PROXY_HOST);
        this.proxyPort = proxyPort != null ? proxyPort
                : Settings.getIntegerSetting(getSettings(), SETTING_PROXY_PORT, null);
        if (this.proxyHost != null && this.proxyPort != null) {
            this.proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(this.proxyHost, this.proxyPort));
        } else {
            this.proxy = null;
        }
        this.httpUserAgent = "jmxtrans-standalone/1 " + "(" + System.getProperty("java.vm.name") + "/"
                + System.getProperty("java.version") + "; " + System.getProperty("os.name") + "-"
                + System.getProperty("os.arch") + "/" + System.getProperty("os.version") + ")";
    }

    @Override
    public void validateSetup(Server server, Query query) throws ValidationException {
        logger.info("Start Librato writer connected to '{}', proxy {} with username '{}' ...", url, proxy,
                username);
    }

    @Override
    public void internalWrite(Server server, Query query, ImmutableList<Result> results) throws Exception {
        logger.debug("Export to '{}', proxy {} metrics {}", url, proxy, query);
        writeToLibrato(server, query, results);
    }

    private void serialize(Server server, Query query, List<Result> results, OutputStream outputStream)
            throws IOException {
        JsonGenerator g = jsonFactory.createGenerator(outputStream, JsonEncoding.UTF8);
        g.writeStartObject();
        g.writeArrayFieldStart("counters");
        g.writeEndArray();

        String source = getSource(server);

        g.writeArrayFieldStart("gauges");
        List<String> typeNames = getTypeNames();
        for (Result result : results) {
            Map<String, Object> resultValues = result.getValues();
            for (Map.Entry<String, Object> values : resultValues.entrySet()) {
                if (isNumeric(values.getValue())) {
                    g.writeStartObject();
                    g.writeStringField("name", KeyUtils.getKeyString(query, result, values, typeNames));
                    if (source != null && !source.isEmpty()) {
                        g.writeStringField("source", source);
                    }
                    g.writeNumberField("measure_time",
                            TimeUnit.SECONDS.convert(result.getEpoch(), TimeUnit.MILLISECONDS));
                    Object value = values.getValue();
                    if (value instanceof Integer) {
                        g.writeNumberField("value", (Integer) value);
                    } else if (value instanceof Long) {
                        g.writeNumberField("value", (Long) value);
                    } else if (value instanceof Float) {
                        g.writeNumberField("value", (Float) value);
                    } else if (value instanceof Double) {
                        g.writeNumberField("value", (Double) value);
                    }
                    g.writeEndObject();
                }
            }
        }
        g.writeEndArray();
        g.writeEndObject();
        g.flush();
        g.close();

    }

    private void writeToLibrato(Server server, Query query, List<Result> results) {
        HttpURLConnection urlConnection = null;
        try {
            if (proxy == null) {
                urlConnection = (HttpURLConnection) url.openConnection();
            } else {
                urlConnection = (HttpURLConnection) url.openConnection(proxy);
            }
            urlConnection.setRequestMethod("POST");
            urlConnection.setDoInput(true);
            urlConnection.setDoOutput(true);
            urlConnection.setReadTimeout(libratoApiTimeoutInMillis);
            urlConnection.setRequestProperty("content-type", "application/json; charset=utf-8");
            urlConnection.setRequestProperty("Authorization", "Basic " + basicAuthentication);
            urlConnection.setRequestProperty("User-Agent", httpUserAgent);

            serialize(server, query, results, urlConnection.getOutputStream());
            int responseCode = urlConnection.getResponseCode();
            if (responseCode != 200) {
                logger.warn("Failure {}:'{}' to send result to Librato server '{}' with proxy {}, username {}",
                        responseCode, urlConnection.getResponseMessage(), url, proxy, username);
            }
            if (logger.isTraceEnabled()) {
                StringWriter out = new StringWriter();
                IOUtils.copy(urlConnection.getInputStream(), out);
                logger.trace(out.toString());
            }
        } catch (Exception e) {
            logger.warn("Failure to send result to Librato server '{}' with proxy {}, username {}", url, proxy,
                    username, e);
        } finally {
            if (urlConnection != null) {
                try {
                    InputStream in = urlConnection.getInputStream();
                    IOUtils.copy(in, NullOutputStream.NULL_OUTPUT_STREAM);
                    IOUtils.closeQuietly(in);
                    InputStream err = urlConnection.getErrorStream();
                    if (err != null) {
                        IOUtils.copy(err, NullOutputStream.NULL_OUTPUT_STREAM);
                        IOUtils.closeQuietly(err);
                    }
                } catch (IOException e) {
                    logger.warn("Exception flushing http connection", e);
                }
            }

        }
    }

    private String getSource(Server server) {
        if (server.getAlias() != null) {
            return server.getAlias();
        } else {
            return StringUtils.cleanupStr(server.getHost());
        }
    }

    public URL getUrl() {
        return url;
    }

    public int getLibratoApiTimeoutInMillis() {
        return libratoApiTimeoutInMillis;
    }

    public String getUsername() {
        return username;
    }

    public String getToken() {
        return token;
    }

    public String getProxyHost() {
        return proxyHost;
    }

    public Integer getProxyPort() {
        return proxyPort;
    }
}