/**
* Copyright 2013-2014 Recruit Technologies Co., Ltd. and contributors
* (see CONTRIBUTORS.md)
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. A copy of the
* License is distributed with this work in the LICENSE.md file. You may
* also obtain a copy of the License from
*
* 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.gennai.gungnir.metrics;
import static org.gennai.gungnir.GungnirConfig.*;
import static org.gennai.gungnir.GungnirConst.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.gennai.gungnir.GungnirConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.timgroup.statsd.NonBlockingStatsDClient;
import com.timgroup.statsd.StatsDClient;
import com.timgroup.statsd.StatsDClientErrorHandler;
import com.timgroup.statsd.StatsDClientException;
import backtype.storm.Config;
import backtype.storm.metric.api.IMetricsConsumer;
import backtype.storm.task.IErrorReporter;
import backtype.storm.task.TopologyContext;
public class StatsdMetricsConsumer implements IMetricsConsumer {
private static final Logger LOG = LoggerFactory.getLogger(StatsdMetricsConsumer.class);
private transient String topologyName;
private transient String statsdHost;
private transient int statsdPort;
private transient String statsdPrefix;
private transient StatsDClient statsd;
@Override
public void prepare(@SuppressWarnings("rawtypes") Map stormConf, Object registrationArgument,
TopologyContext context, IErrorReporter errorReporter) {
@SuppressWarnings("unchecked")
GungnirConfig conf = GungnirConfig.wrap((Map<String, Object>) stormConf.get(GUNGNIR_CONFIG));
topologyName = (String) stormConf.get(Config.TOPOLOGY_NAME);
statsdHost = conf.getString(METRICS_STATSD_HOST);
statsdPort = conf.getInteger(METRICS_STATSD_PORT);
statsdPrefix = conf.getString(METRICS_CONSUMER_PREFIX);
if (!statsdPrefix.endsWith(".")) {
statsdPrefix += ".";
}
LOG.info("Metrics to StatsD[{}:{} {}]", statsdHost, statsdPort, statsdPrefix);
try {
statsd = new NonBlockingStatsDClient(statsdPrefix + clean(topologyName),
statsdHost, statsdPort, new StatsDClientErrorHandler() {
@Override
public void handle(Exception e) {
LOG.warn("can't send to StatsD", e);
}
});
} catch (StatsDClientException e) {
LOG.error("Failed to connect StatsD", e);
statsd = null;
}
}
private String clean(String s) {
return s.replace('.', '_').replace('/', '_').replace(':', '.');
}
@Override
public void handleDataPoints(TaskInfo taskInfo, Collection<DataPoint> dataPoints) {
if (statsd != null) {
StringBuilder sb = new StringBuilder()
.append(clean(taskInfo.srcWorkerHost)).append('.')
.append(taskInfo.srcWorkerPort).append('.')
.append(clean(taskInfo.srcComponentId)).append('.');
int hdrLength = sb.length();
for (DataPoint p : dataPoints) {
sb.delete(hdrLength, sb.length());
sb.append(p.name);
report(sb.toString(), p.value);
}
}
}
public void report(String s, Object o) {
LOG.debug("reporting: {}={}", s, o);
if (o instanceof Float) {
statsd.gauge(s, ((Float) o).doubleValue());
} else if (o instanceof Double) {
statsd.gauge(s, (Double) o);
} else if (o instanceof Byte) {
statsd.gauge(s, ((Byte) o).longValue());
} else if (o instanceof Short) {
statsd.gauge(s, ((Short) o).longValue());
} else if (o instanceof Integer) {
statsd.gauge(s, ((Integer) o).longValue());
} else if (o instanceof Long) {
statsd.gauge(s, (Long) o);
} else if (o instanceof BigInteger) {
statsd.gauge(s, ((BigInteger) o).longValue());
} else if (o instanceof BigDecimal) {
statsd.gauge(s, ((BigDecimal) o).doubleValue());
} else if (o instanceof Map) {
@SuppressWarnings("unchecked")
Map<Object, Object> map = (Map<Object, Object>) o;
StringBuilder sb = new StringBuilder(s);
int hdrLength = sb.length();
for (Object subName : map.keySet()) {
sb.delete(hdrLength, sb.length());
sb.append('.').append(clean(subName.toString()));
report(sb.toString(), map.get(subName));
}
} else if (o instanceof List) {
@SuppressWarnings("unchecked")
List<Object> list = (List<Object>) o;
int i = 0;
Iterator<Object> ite = null;
StringBuilder sb = new StringBuilder(s);
int hdrLength = sb.length();
for (ite = list.iterator(); ite.hasNext();) {
sb.delete(hdrLength, sb.length());
sb.append('[').append(i).append(']');
report(sb.toString(), ite.next());
i++;
}
} else {
LOG.warn("Metrics {}={}", s, o);
}
}
@Override
public void cleanup() {
if (statsd != null) {
statsd.stop();
}
}
}