/* * Copyright 2013 Rackspace * * 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.rackspacecloud.blueflood.io; import com.codahale.metrics.Histogram; import com.codahale.metrics.Meter; import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.Timer; import com.datastax.shaded.netty.util.internal.StringUtil; import com.netflix.astyanax.connectionpool.exceptions.ConnectionException; import com.netflix.astyanax.connectionpool.exceptions.PoolTimeoutException; import com.rackspacecloud.blueflood.utils.Metrics; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.management.MBeanServer; import javax.management.ObjectName; import java.lang.management.ManagementFactory; public class Instrumentation implements InstrumentationMBean { private static final Logger log = LoggerFactory.getLogger(Instrumentation.class); private static ReadTimers readTimers = new ReadTimers(); private static WriteTimers writeTimers = new WriteTimers(); private static final Meter writeErrMeter; private static final Meter readErrMeter; private static final Meter batchReadErrMeter; // One-off meters private static final Meter allPoolsExhaustedException; private static final Meter fullResMetricWritten; private static final Meter fullResPreaggregatedMetricWritten; private static final Meter metricsWithShortDelayReceived; private static final Meter metricsWithLongDelayReceived; private static final Histogram rawPointsIn5Min; static { Class kls = Instrumentation.class; writeErrMeter = Metrics.meter(kls, "writes", "Cassandra Write Errors"); readErrMeter = Metrics.meter(kls, "reads", "Cassandra Read Errors"); batchReadErrMeter = Metrics.meter(kls, "reads", "Batch Cassandra Read Errors"); allPoolsExhaustedException = Metrics.meter(kls, "All Pools Exhausted"); fullResMetricWritten = Metrics.meter(kls, "Full Resolution Metrics Written"); fullResPreaggregatedMetricWritten = Metrics.meter(kls, "Full Resolution Preaggregated Metrics Written"); metricsWithShortDelayReceived = Metrics.meter(kls, "Metrics with short delay received"); metricsWithLongDelayReceived = Metrics.meter(kls, "Metrics with long delay received"); rawPointsIn5Min = Metrics.histogram(kls, "Raw points in 5 min"); try { final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); final String name = String.format("com.rackspacecloud.blueflood.io:type=%s", Instrumentation.class.getSimpleName()); final ObjectName nameObj = new ObjectName(name); mbs.registerMBean(new Instrumentation() { }, nameObj); } catch (Exception exc) { log.error("Unable to register mbean for " + Instrumentation.class.getSimpleName(), exc); } } private Instrumentation() {/* Used for JMX exposure */} public static Timer.Context getReadTimerContext(String queryCF) { return readTimers.getTimerContext(queryCF, false); } public static Timer.Context getBatchReadTimerContext(String queryCF) { return readTimers.getTimerContext(queryCF, true); } public static Timer.Context getWriteTimerContext(String queryCF) { return writeTimers.getTimerContext(queryCF, false); } public static Timer.Context getBatchWriteTimerContext(String queryCF) { return writeTimers.getTimerContext(queryCF, true); } // Most error tracking is done in InstrumentedConnectionPoolMonitor // However, some issues can't be properly tracked using that alone. // For example, there is no good way to differentiate (in the connectionpoolmonitor) // a PoolTimeoutException indicating that Astyanax has to look in another host-specific-pool // to find a connection from one indicating Astyanax already looked in every pool and is // going to bubble up the exception to our reader/writer public static void markReadError() { readErrMeter.mark(); } public static void markBatchReadError(ConnectionException e) { batchReadErrMeter.mark(); if (e instanceof PoolTimeoutException) { allPoolsExhaustedException.mark(); } } /** * Caller should explicitly call {@link com.rackspacecloud.blueflood.io.Instrumentation#markPoolExhausted()} * and {@link com.rackspacecloud.blueflood.io.Instrumentation#markReadError()} * @param e */ @Deprecated public static void markReadError(ConnectionException e) { markReadError(); if (e instanceof PoolTimeoutException) { allPoolsExhaustedException.mark(); } } public static void markPoolExhausted() { allPoolsExhaustedException.mark(); } public static void markWriteError() { writeErrMeter.mark(); } public static void markWriteError(ConnectionException e) { markWriteError(); if (e instanceof PoolTimeoutException) { allPoolsExhaustedException.mark(); } } private static class ReadTimers { public Timer.Context getTimerContext(String queryCF, boolean batch) { final String metricName = (batch ? MetricRegistry.name("batched-", queryCF) : queryCF); final Timer timer = Metrics.timer(Instrumentation.class, "reads", metricName); return timer.time(); } } private static class WriteTimers { public Timer.Context getTimerContext(String queryCF, boolean batch) { final String metricName = (batch ? MetricRegistry.name("batched", queryCF) : queryCF); final Timer timer = Metrics.timer(Instrumentation.class, "writes", metricName); return timer.time(); } } public static void markNotFound(String columnFamilyName) { final Meter meter = Metrics.meter(Instrumentation.class, "reads", "Not Found", columnFamilyName); meter.mark(); } public static void markFullResMetricWritten() { fullResMetricWritten.mark(); } public static void markFullResPreaggregatedMetricWritten() { fullResPreaggregatedMetricWritten.mark(); } public static void markMetricsWithShortDelayReceived() { metricsWithShortDelayReceived.mark(); } public static void markMetricsWithLongDelayReceived() { metricsWithLongDelayReceived.mark(); } public static Meter getIngestedMetricsMeter(String tenantId) { if ( StringUtil.isNullOrEmpty(tenantId) ) { return null; } return Metrics.meter(Instrumentation.class, "tenants", tenantId, "Data Points Ingested"); } public static Meter getIngestedDelayedMetricsMeter(String tenantId) { if ( StringUtil.isNullOrEmpty(tenantId) ) { return null; } return Metrics.meter(Instrumentation.class, "tenants", tenantId, "Delayed Data Points Ingested"); } public static Histogram getRawPointsIn5MinHistogram() { return rawPointsIn5Min; } }