/*
* Copyright (c) 2014-2015 Spotify AB
*
* Licensed 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 com.spotify.folsom.client;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.spotify.folsom.GetResult;
import com.spotify.folsom.MemcacheStatus;
import com.spotify.folsom.Metrics;
import com.yammer.metrics.core.Gauge;
import com.yammer.metrics.core.Meter;
import com.yammer.metrics.core.MetricName;
import com.yammer.metrics.core.MetricsRegistry;
import com.yammer.metrics.core.Timer;
import com.yammer.metrics.core.TimerContext;
import java.util.List;
import static java.util.concurrent.TimeUnit.SECONDS;
public class YammerMetrics implements Metrics {
public static final String GROUP = "com.spotify.folsom";
private final Timer gets;
private final Meter getHits;
private final Meter getMisses;
private final Meter getSuccesses;
private final Meter getFailures;
private final Timer multigets;
private final Meter multigetSuccesses;
private final Meter multigetFailures;
private final Timer sets;
private final Meter setSuccesses;
private final Meter setFailures;
private final Timer deletes;
private final Meter deleteSuccesses;
private final Meter deleteFailures;
private final Timer incrDecrs;
private final Meter incrDecrSuccesses;
private final Meter incrDecrFailures;
private final Timer touches;
private final Meter touchSuccesses;
private final Meter touchFailures;
private volatile OutstandingRequestsGauge internalOutstandingReqGauge;
private final Gauge<Integer> outstandingRequestsGauge;
public YammerMetrics(final MetricsRegistry registry) {
this.gets = registry.newTimer(name("get", "requests"), SECONDS, SECONDS);
this.getSuccesses = registry.newMeter(name("get", "successes"), "Successes", SECONDS);
this.getHits = registry.newMeter(name("get", "hits"), "Hits", SECONDS);
this.getMisses = registry.newMeter(name("get", "misses"), "Misses", SECONDS);
this.getFailures = registry.newMeter(name("get", "failures"), "Failures", SECONDS);
this.multigets = registry.newTimer(name("multiget", "requests"), SECONDS, SECONDS);
this.multigetSuccesses = registry.newMeter(name("multiget", "successes"), "Successes", SECONDS);
this.multigetFailures = registry.newMeter(name("multiget", "failures"), "Failures", SECONDS);
this.sets = registry.newTimer(name("set", "requests"), SECONDS, SECONDS);
this.setSuccesses = registry.newMeter(name("set", "successes"), "Successes", SECONDS);
this.setFailures = registry.newMeter(name("set", "failures"), "Failures", SECONDS);
this.deletes = registry.newTimer(name("delete", "requests"), SECONDS, SECONDS);
this.deleteSuccesses = registry.newMeter(name("delete", "successes"), "Successes", SECONDS);
this.deleteFailures = registry.newMeter(name("delete", "failures"), "Failures", SECONDS);
this.incrDecrs = registry.newTimer(name("incrdecr", "requests"), SECONDS, SECONDS);
this.incrDecrSuccesses = registry.newMeter(name("incrdecr", "successes"), "Successes", SECONDS);
this.incrDecrFailures = registry.newMeter(name("incrdecr", "failures"), "Failures", SECONDS);
this.touches = registry.newTimer(name("touch", "requests"), SECONDS, SECONDS);
this.touchSuccesses = registry.newMeter(name("touch", "successes"), "Successes", SECONDS);
this.touchFailures = registry.newMeter(name("touch", "failures"), "Failures", SECONDS);
final MetricName gaugeName = name("outstandingRequests", "count");
this.outstandingRequestsGauge = registry.newGauge(gaugeName, new Gauge<Integer>() {
@Override
public Integer value() {
return internalOutstandingReqGauge != null ?
internalOutstandingReqGauge.getOutstandingRequests() : 0;
}
});
}
private MetricName name(final String type, final String name) {
return new MetricName(GROUP, type, name);
}
@Override
public void measureGetFuture(ListenableFuture<GetResult<byte[]>> future) {
final TimerContext ctx = gets.time();
final FutureCallback<GetResult<byte[]>> metricsCallback =
new FutureCallback<GetResult<byte[]>>() {
@Override
public void onSuccess(final GetResult<byte[]> result) {
getSuccesses.mark();
if (result != null) {
getHits.mark();
} else {
getMisses.mark();
}
ctx.stop();
}
@Override
public void onFailure(final Throwable t) {
getFailures.mark();
ctx.stop();
}
};
Futures.addCallback(future, metricsCallback, Utils.SAME_THREAD_EXECUTOR);
}
@Override
public void measureMultigetFuture(ListenableFuture<List<GetResult<byte[]>>> future) {
final TimerContext ctx = multigets.time();
final FutureCallback<List<GetResult<byte[]>>> metricsCallback =
new FutureCallback<List<GetResult<byte[]>>>() {
@Override
public void onSuccess(final List<GetResult<byte[]>> result) {
multigetSuccesses.mark();
int hits = 0;
int total = result.size();
for (int i = 0; i < total; i++) {
if (result.get(i) != null) {
hits++;
}
}
getHits.mark(hits);
getMisses.mark(total - hits);
ctx.stop();
}
@Override
public void onFailure(final Throwable t) {
multigetFailures.mark();
ctx.stop();
}
};
Futures.addCallback(future, metricsCallback, Utils.SAME_THREAD_EXECUTOR);
}
@Override
public void measureDeleteFuture(ListenableFuture<MemcacheStatus> future) {
final TimerContext ctx = deletes.time();
final FutureCallback<MemcacheStatus> metricsCallback = new FutureCallback<MemcacheStatus>() {
@Override
public void onSuccess(final MemcacheStatus result) {
deleteSuccesses.mark();
ctx.stop();
}
@Override
public void onFailure(final Throwable t) {
deleteFailures.mark();
ctx.stop();
}
};
Futures.addCallback(future, metricsCallback, Utils.SAME_THREAD_EXECUTOR);
}
@Override
public void measureSetFuture(ListenableFuture<MemcacheStatus> future) {
final TimerContext ctx = sets.time();
final FutureCallback<MemcacheStatus> metricsCallback = new FutureCallback<MemcacheStatus>() {
@Override
public void onSuccess(final MemcacheStatus result) {
setSuccesses.mark();
ctx.stop();
}
@Override
public void onFailure(final Throwable t) {
setFailures.mark();
ctx.stop();
}
};
Futures.addCallback(future, metricsCallback, Utils.SAME_THREAD_EXECUTOR);
}
@Override
public void measureIncrDecrFuture(ListenableFuture<Long> future) {
final TimerContext ctx = incrDecrs.time();
final FutureCallback<Long> metricsCallback = new FutureCallback<Long>() {
@Override
public void onSuccess(final Long result) {
incrDecrSuccesses.mark();
ctx.stop();
}
@Override
public void onFailure(final Throwable t) {
incrDecrFailures.mark();
ctx.stop();
}
};
Futures.addCallback(future, metricsCallback, Utils.SAME_THREAD_EXECUTOR);
}
@Override
public void measureTouchFuture(ListenableFuture<MemcacheStatus> future) {
final TimerContext ctx = touches.time();
final FutureCallback<MemcacheStatus> metricsCallback = new FutureCallback<MemcacheStatus>() {
@Override
public void onSuccess(final MemcacheStatus result) {
touchSuccesses.mark();
ctx.stop();
}
@Override
public void onFailure(final Throwable t) {
touchFailures.mark();
ctx.stop();
}
};
Futures.addCallback(future, metricsCallback, Utils.SAME_THREAD_EXECUTOR);
}
@Override
public void registerOutstandingRequestsGauge(OutstandingRequestsGauge gauge) {
this.internalOutstandingReqGauge = gauge;
}
public Timer getGets() {
return gets;
}
public Meter getGetHits() {
return getHits;
}
public Meter getGetMisses() {
return getMisses;
}
public Meter getGetSuccesses() {
return getSuccesses;
}
public Meter getGetFailures() {
return getFailures;
}
public Timer getMultigets() {
return multigets;
}
public Meter getMultigetSuccesses() {
return multigetSuccesses;
}
public Meter getMultigetFailures() {
return multigetFailures;
}
public Timer getSets() {
return sets;
}
public Meter getSetSuccesses() {
return setSuccesses;
}
public Meter getSetFailures() {
return setFailures;
}
public Timer getDeletes() {
return deletes;
}
public Meter getDeleteSuccesses() {
return deleteSuccesses;
}
public Meter getDeleteFailures() {
return deleteFailures;
}
public Timer getIncrDecrs() {
return incrDecrs;
}
public Meter getIncrDecrSuccesses() {
return incrDecrSuccesses;
}
public Meter getIncrDecrFailures() {
return incrDecrFailures;
}
public Timer getTouches() {
return touches;
}
public Meter getTouchSuccesses() {
return touchSuccesses;
}
public Meter getTouchFailures() {
return touchFailures;
}
public Gauge<Integer> getOutstandingRequestsGauge() {
return outstandingRequestsGauge;
}
}