/* * Copyright (C) 2012 Facebook, Inc. * * 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.facebook.stats; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; /* Simple class to periodically gather a service's counters and cache them. * How to use: * * When your thrift service initializes: * * this.counterCacher = new CounterCacher(this); * this.counterCacher.run(); * * Then implement getCounters() as something like: * * public Map<String, Long> getCounters() { * return this.counterCacher.getCounters(); * } * * Make sure you implement your own counter-generating code inside * makeCounters(), and have it call super.makeCounters() so that fb303 can * do the same. * */ public class CounterCacher { private static class ThreadFactory implements java.util.concurrent.ThreadFactory { long count = 0; public Thread newThread(Runnable r) { count ++; return new Thread(threadGroup, r, threadGroup.getName() + "-" + count); } } private class CounterCacherRunner implements Runnable { private final long minWait; private final long maxWait; public CounterCacherRunner(long minWait, long maxWait) { this.minWait = minWait; this.maxWait = maxWait; } public void run() { final Logger log = Logger.getLogger(CounterCacher.class.getCanonicalName()); wantRunning = true; running = true; log.log(Level.INFO, "Cacheing counters every " + minWait + " - " + maxWait + " msec"); try { while(wantRunning) { try { long startTime = System.currentTimeMillis(); counters = reporter.makeCounters(); Thread.sleep(minWait); long runTime = System.currentTimeMillis() - startTime; long remainingWait = maxWait - runTime; if(remainingWait > 0) { Thread.sleep(remainingWait); } } catch(InterruptedException iex) { wantRunning = false; } } } catch(RuntimeException rex) { log.log(Level.SEVERE, "RuntimeException thrown while running makeCounters()", rex); } finally { running = false; } } } private final static ThreadGroup threadGroup = new ThreadGroup("CounterCacher"); private final static ThreadFactory threadFactory = new ThreadFactory(); private volatile Thread thread; private final Runnable runnable; private final FacebookStatsReporter reporter; private volatile boolean running = false; private volatile boolean wantRunning = false; private volatile Map<String, Long> counters; /** * @param FacebookStatsReporter Your service * @param long minWait Minimum time to wait between calls * to makeCounters (default=1000, or 1s) * @param long maxWait Maximum time to wait between calls * to makeCounters (default=1000, or 1s) * * Example: * * If it takes 2 seconds to make your counters, minWait is 1 second, * and maxWait is 10 seconds then there will be an 8 second delay * between calls. If it takes 15 seconds to make your counters, there * will be a 1 second delay. */ public CounterCacher(final FacebookStatsReporter reporter, long minWait, long maxWait) { runnable = new CounterCacherRunner(minWait, maxWait); this.reporter = reporter; } public CounterCacher(FacebookStatsReporter reporter, long minWait) { this(reporter, minWait, minWait); } public CounterCacher(FacebookStatsReporter reporter) { this(reporter, 1000); } public void start() { if(running) { throw new IllegalStateException("start() called while already running!"); } thread = threadFactory.newThread(runnable); thread.start(); } public void stop() { if(!running) { throw new IllegalStateException("stop() called while not running!"); } wantRunning = false; thread.interrupt(); try { thread.join(); } catch(InterruptedException iex) { } thread = null; } public Map<String, Long> getCounters() { return counters; } }