org.apache.htrace.impl.Conf.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.htrace.impl.Conf.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.htrace.impl;

import java.io.IOException;
import java.io.File;
import java.net.InetSocketAddress;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.apache.htrace.core.HTraceConfiguration;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * The configuration of the HTracedSpanReceiver.
 *
 * This class extracts all the relevant configuration information for
 * HTracedSpanReceiver from the HTraceConfiguration.  It performs parsing and
 * bounds-checking for the configuration keys.
 *
 * It is more efficient to store the configuration as final values in this
 * structure than to access the HTraceConfiguration object directly.  This is
 * especially true when the HTraceConfiguration object is a thin shim around a
 * Hadoop Configuration object, which requires synchronization to access.
 */
class Conf {
    private static final Log LOG = LogFactory.getLog(Conf.class);

    /**
     * Address of the htraced server.
     */
    final static String ADDRESS_KEY = "htraced.receiver.address";

    /**
     * The minimum number of milliseconds to wait for a read or write
     * operation on the network.
     */
    final static String IO_TIMEOUT_MS_KEY = "htraced.receiver.io.timeout.ms";
    final static int IO_TIMEOUT_MS_DEFAULT = 60000;
    final static int IO_TIMEOUT_MS_MIN = 50;

    /**
     * The minimum number of milliseconds to wait for a network
     * connection attempt.
     */
    final static String CONNECT_TIMEOUT_MS_KEY = "htraced.receiver.connect.timeout.ms";
    final static int CONNECT_TIMEOUT_MS_DEFAULT = 60000;
    final static int CONNECT_TIMEOUT_MS_MIN = 50;

    /**
     * The minimum number of milliseconds to keep alive a connection when it's
     * not in use.
     */
    final static String IDLE_TIMEOUT_MS_KEY = "htraced.receiver.idle.timeout.ms";
    final static int IDLE_TIMEOUT_MS_DEFAULT = 60000;
    final static int IDLE_TIMEOUT_MS_MIN = 0;

    /**
     * Configure the retry times to use when an attempt to flush spans to
     * htraced fails.  This is configured as a comma-separated list of delay
     * times in milliseconds.  If the configured value is empty, no retries
     * will be made.
     */
    final static String FLUSH_RETRY_DELAYS_KEY = "htraced.flush.retry.delays.key";
    final static String FLUSH_RETRY_DELAYS_DEFAULT = "1000,30000";

    /**
     * The maximum length of time to go in between flush attempts.
     * Once this time elapses, a flush will be triggered even if we don't
     * have that many spans buffered.
     */
    final static String MAX_FLUSH_INTERVAL_MS_KEY = "htraced.receiver.max.flush.interval.ms";
    final static int MAX_FLUSH_INTERVAL_MS_DEFAULT = 60000;
    final static int MAX_FLUSH_INTERVAL_MS_MIN = 10;

    /**
     * Whether or not to use msgpack for span serialization.
     * If this key is false, JSON over REST will be used.
     * If this key is true, msgpack over custom RPC will be used.
     */
    final static String PACKED_KEY = "htraced.receiver.packed";
    final static boolean PACKED_DEFAULT = true;

    /**
     * The size of the span buffers.
     */
    final static String BUFFER_SIZE_KEY = "htraced.receiver.buffer.size";
    final static int BUFFER_SIZE_DEFAULT = 16 * 1024 * 1024;
    static int BUFFER_SIZE_MIN = 4 * 1024 * 1024;
    // The maximum buffer size should not be longer than
    // PackedBuffer.MAX_HRPC_BODY_LENGTH.
    final static int BUFFER_SIZE_MAX = 32 * 1024 * 1024;

    /**
     * Set the fraction of the span buffer which needs to fill up before we
     * will automatically trigger a flush.  This is a fraction, not a percentage.
     * It is between 0 and 1.
     */
    final static String BUFFER_SEND_TRIGGER_FRACTION_KEY = "htraced.receiver.buffer.send.trigger.fraction";
    final static double BUFFER_SEND_TRIGGER_FRACTION_DEFAULT = 0.5;
    final static double BUFFER_SEND_TRIGGER_FRACTION_MIN = 0.1;

    /**
     * The length of time which receiveSpan should wait for a free spot in a
     * span buffer before giving up and dropping the span
     */
    final static String SPAN_DROP_TIMEOUT_MS_KEY = "htraced.max.buffer.full.retry.ms.key";
    final static int SPAN_DROP_TIMEOUT_MS_DEFAULT = 5000;

    /**
     * The length of time we should wait between displaying log messages on the
     * rate-limited loggers.
     */
    final static String ERROR_LOG_PERIOD_MS_KEY = "htraced.error.log.period.ms";
    final static long ERROR_LOG_PERIOD_MS_DEFAULT = 30000L;

    final static String DROPPED_SPANS_LOG_PATH_KEY = "htraced.dropped.spans.log.path";

    final static String DROPPED_SPANS_LOG_PATH_DEFAULT = new File(System.getProperty("java.io.tmpdir", "/tmp"),
            "htraceDropped").getAbsolutePath();

    final static String DROPPED_SPANS_LOG_MAX_SIZE_KEY = "htraced.dropped.spans.log.max.size";

    final static long DROPPED_SPANS_LOG_MAX_SIZE_DEFAULT = 1024L * 1024L;

    @JsonProperty("ioTimeoutMs")
    final int ioTimeoutMs;

    @JsonProperty("connectTimeoutMs")
    final int connectTimeoutMs;

    @JsonProperty("idleTimeoutMs")
    final int idleTimeoutMs;

    @JsonProperty("flushRetryDelays")
    final int[] flushRetryDelays;

    @JsonProperty("maxFlushIntervalMs")
    final int maxFlushIntervalMs;

    @JsonProperty("packed")
    final boolean packed;

    @JsonProperty("bufferSize")
    final int bufferSize;

    @JsonProperty("spanDropTimeoutMs")
    final int spanDropTimeoutMs;

    @JsonProperty("errorLogPeriodMs")
    final long errorLogPeriodMs;

    @JsonProperty("triggerSize")
    final int triggerSize;

    @JsonProperty("endpointStr")
    final String endpointStr;

    @JsonProperty("endpoint")
    final InetSocketAddress endpoint;

    @JsonProperty("droppedSpansLogPath")
    final String droppedSpansLogPath;

    @JsonProperty("droppedSpansLogMaxSize")
    final long droppedSpansLogMaxSize;

    private static int getBoundedInt(final HTraceConfiguration conf, String key, int defaultValue, int minValue,
            int maxValue) {
        int val = conf.getInt(key, defaultValue);
        if (val < minValue) {
            LOG.warn(
                    "Can't set " + key + " to " + val + ".  Using minimum value " + "of " + minValue + " instead.");
            return minValue;
        } else if (val > maxValue) {
            LOG.warn(
                    "Can't set " + key + " to " + val + ".  Using maximum value " + "of " + maxValue + " instead.");
            return maxValue;
        }
        return val;
    }

    private static long getBoundedLong(final HTraceConfiguration conf, String key, long defaultValue, long minValue,
            long maxValue) {
        String strVal = conf.get(key, Long.toString(defaultValue));
        long val = 0;
        try {
            val = Long.parseLong(strVal);
        } catch (NumberFormatException nfe) {
            throw new IllegalArgumentException("Bad value for '" + key + "': should be long");
        }
        if (val < minValue) {
            LOG.warn(
                    "Can't set " + key + " to " + val + ".  Using minimum value " + "of " + minValue + " instead.");
            return minValue;
        } else if (val > maxValue) {
            LOG.warn(
                    "Can't set " + key + " to " + val + ".  Using maximum value " + "of " + maxValue + " instead.");
            return maxValue;
        }
        return val;
    }

    private static double getBoundedDouble(final HTraceConfiguration conf, String key, double defaultValue,
            double minValue, double maxValue) {
        String strVal = conf.get(key, Double.toString(defaultValue));
        double val = 0;
        try {
            val = Double.parseDouble(strVal);
        } catch (NumberFormatException nfe) {
            throw new IllegalArgumentException("Bad value for '" + key + "': should be double");
        }
        if (val < minValue) {
            LOG.warn(
                    "Can't set " + key + " to " + val + ".  Using minimum value " + "of " + minValue + " instead.");
            return minValue;
        }
        if (val > maxValue) {
            LOG.warn(
                    "Can't set " + key + " to " + val + ".  Using maximum value " + "of " + maxValue + " instead.");
            return maxValue;
        }
        return val;
    }

    private static int parseColonPort(String portStr) throws IOException {
        int colonPosition = portStr.indexOf(':');
        if (colonPosition != 0) {
            throw new IOException("Invalid port string " + portStr);
        }
        int port = Integer.parseInt(portStr.substring(1), 10);
        if ((port < 0) || (port > 65535)) {
            throw new IOException("Invalid port number " + port);
        }
        return port;
    }

    /**
     * Parse a hostname:port or ip:port pair.
     *
     * @param str       The string to parse.
     * @return          The socket address.
     */
    InetSocketAddress parseHostPortPair(String str) throws IOException {
        str = str.trim();
        if (str.isEmpty()) {
            throw new IOException("No hostname:port pair given.");
        }
        int bracketBegin = str.indexOf('[');
        if (bracketBegin == 0) {
            // Parse an ipv6-style address enclosed in square brackets.
            int bracketEnd = str.indexOf(']');
            if (bracketEnd < 0) {
                throw new IOException("Found left bracket, but no corresponding " + "right bracket, in " + str);
            }
            String host = str.substring(bracketBegin + 1, bracketEnd);
            int port = parseColonPort(str.substring(bracketEnd + 1));
            return InetSocketAddress.createUnresolved(host, port);
        } else if (bracketBegin > 0) {
            throw new IOException(
                    "Found a left bracket that wasn't at the " + "start of the host:port pair in " + str);
        } else {
            int colon = str.indexOf(':');
            if (colon <= 0) {
                throw new IOException("No port component found in " + str);
            }
            String host = str.substring(0, colon);
            int port = parseColonPort(str.substring(colon));
            return InetSocketAddress.createUnresolved(host, port);
        }
    }

    static int[] getIntArray(String arrayStr) {
        String[] array = arrayStr.split(",");
        int nonEmptyEntries = 0;
        for (String str : array) {
            if (!str.trim().isEmpty()) {
                nonEmptyEntries++;
            }
        }
        int[] ret = new int[nonEmptyEntries];
        int i = 0;
        for (String str : array) {
            if (!str.trim().isEmpty()) {
                ret[i++] = Integer.parseInt(str);
            }
        }
        return ret;
    }

    Conf(HTraceConfiguration conf) throws IOException {
        this.ioTimeoutMs = getBoundedInt(conf, IO_TIMEOUT_MS_KEY, IO_TIMEOUT_MS_DEFAULT, IO_TIMEOUT_MS_MIN,
                Integer.MAX_VALUE);
        this.connectTimeoutMs = getBoundedInt(conf, CONNECT_TIMEOUT_MS_KEY, CONNECT_TIMEOUT_MS_DEFAULT,
                CONNECT_TIMEOUT_MS_MIN, Integer.MAX_VALUE);
        this.idleTimeoutMs = getBoundedInt(conf, IDLE_TIMEOUT_MS_KEY, IDLE_TIMEOUT_MS_DEFAULT, IDLE_TIMEOUT_MS_MIN,
                Integer.MAX_VALUE);
        this.flushRetryDelays = getIntArray(conf.get(FLUSH_RETRY_DELAYS_KEY, FLUSH_RETRY_DELAYS_DEFAULT));
        this.maxFlushIntervalMs = getBoundedInt(conf, MAX_FLUSH_INTERVAL_MS_KEY, MAX_FLUSH_INTERVAL_MS_DEFAULT,
                MAX_FLUSH_INTERVAL_MS_MIN, Integer.MAX_VALUE);
        this.packed = conf.getBoolean(PACKED_KEY, PACKED_DEFAULT);
        this.bufferSize = getBoundedInt(conf, BUFFER_SIZE_KEY, BUFFER_SIZE_DEFAULT, BUFFER_SIZE_MIN,
                BUFFER_SIZE_MAX);
        double triggerFraction = getBoundedDouble(conf, BUFFER_SEND_TRIGGER_FRACTION_KEY,
                BUFFER_SEND_TRIGGER_FRACTION_DEFAULT, BUFFER_SEND_TRIGGER_FRACTION_MIN, 1.0);
        this.spanDropTimeoutMs = conf.getInt(SPAN_DROP_TIMEOUT_MS_KEY, SPAN_DROP_TIMEOUT_MS_DEFAULT);
        this.errorLogPeriodMs = getBoundedLong(conf, ERROR_LOG_PERIOD_MS_KEY, ERROR_LOG_PERIOD_MS_DEFAULT, 0,
                Long.MAX_VALUE);
        this.triggerSize = (int) (this.bufferSize * triggerFraction);
        try {
            this.endpointStr = conf.get(ADDRESS_KEY, "");
            this.endpoint = parseHostPortPair(endpointStr);
        } catch (IOException e) {
            throw new IOException("Error reading " + ADDRESS_KEY + ": " + e.getMessage());
        }
        this.droppedSpansLogPath = conf.get(DROPPED_SPANS_LOG_PATH_KEY, DROPPED_SPANS_LOG_PATH_DEFAULT);
        this.droppedSpansLogMaxSize = getBoundedLong(conf, DROPPED_SPANS_LOG_MAX_SIZE_KEY,
                DROPPED_SPANS_LOG_MAX_SIZE_DEFAULT, 0, Long.MAX_VALUE);
    }

    @Override
    public String toString() {
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        try {
            return mapper.writeValueAsString(this);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
}