com.facebook.stats.StatsManager Maven / Gradle / Ivy
 The newest version!
        
        /*
 * 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.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import com.facebook.logging.Logger;
import com.facebook.logging.LoggerImpl;
/**
 * This was written to resemble some older libraries.  You may find
 * more functionality in StatsUtil.java.
 *
 * See com.facebook.fb303.stats.HistoryManager for more info on how
 * this works.
 *
 * The parameters "shortName" refer to a stat name such as "queries"
 * or "cpu_load", and "fullName" refer to the longer
 * "queries.sum.600", "cpu_load.avg.3600".
 */
/**
 * Each stat's name corresponds to a single underlying counter, but
 * each counter can be exported for different uses (ExportTypes). For
 * details see com.facebook.fb303.stats.HistoryManager.
 */
public class StatsManager implements HistoryManager {
  private static Logger logger = LoggerImpl.getLogger(StatsManager.class);
  private ConcurrentHashMap typeMap;
  // todo: handle mutliple shortName/types
  private ConcurrentHashMap counterMap;
  /**
   * Creates a StatsMgr instance with default settings for the internal
   * ConcurrentHashMaps.
   */
  public StatsManager() {
    this(16, 0.75f, 16);
  }
  /**
   * Creates a StatsMgr instance with the specified initial capacity and
   * concurrency level for the ConcurrentHashMaps used internally.
   *
   * @param initialNumKeys:  expected initial number of keys
   * @param loadFactor:  load factor of the internal hash maps
   * @param concurrencyLevel:  estimated number of concurrently updating threads
   */
  public StatsManager(int initialNumKeys,
                      float loadFactor,
                      int concurrencyLevel)
  {
    logger.trace("StatsMgr Created");
    this.typeMap =
      new ConcurrentHashMap(initialNumKeys,
                                             loadFactor,
                                             concurrencyLevel);
    this.counterMap =
      new ConcurrentHashMap(initialNumKeys,
                                                      loadFactor,
                                                      concurrencyLevel);
  }
  private void ensureStat(String shortName) {
    if (counterMap.containsKey(shortName)) {
      return;
    }
    MultiWindowGauge mwg = new MultiWindowGauge();
    MultiWindowGauge wasGauge = counterMap.putIfAbsent(shortName, mwg);
    if (wasGauge == null) {
      if (logger.isDebugEnabled()) {
        logger.debug("Created stat " + shortName);
      }
    } else {
      if (logger.isTraceEnabled()) {
        logger.trace("almost accidentally created stat" + shortName
                + " twice.  phew...");
      }
    }
  }
  private void ensureType(String shortName, ExportType etype) {
    boolean hasType = typeMap.containsKey(shortName);
    if (!hasType) {
      Integer prior = typeMap.putIfAbsent(shortName, etype.value());
      if (prior == null) {
        return;
      }
      // some other thread got there first
    }
    Integer bitmask = ExportType.NONE.value();
    boolean done;
    Integer newValue;
    int tries = 0;
    do {
      bitmask = typeMap.get(shortName);
      newValue = bitmask | etype.value();
      if (bitmask == newValue) {
        break;
      }
      done = typeMap.replace(shortName, bitmask, newValue);
      tries++;
    } while (!done);
    if (logger.isTraceEnabled()) {
      logger.trace("Updated type for " + shortName + ", added " + etype
          + ", was " + bitmask + ", now " + newValue + " (after " + tries
          + " tries)");
    }
    return;
  }
  @Override
public void addStatExportType(String shortName, ExportType etype) {
    if (shortName == null) {
      logger.error("Null value passed as key");
      return;
    }
    if (etype == null) {
      logger.error("Null value passed as exportType");
      return;
    }
    ensureStat(shortName);
    ensureType(shortName, etype);
  }
  @Override
public void addStatValue(String shortName, long delta) {
    if (!counterMap.containsKey(shortName)) {
      addStatExportType(shortName, ExportType.AVG);
    }
    MultiWindowGauge stat = counterMap.get(shortName);
    stat.add(delta);
  }
  // fb303
  public long getCounter(String fullName) {
    // todo: convert to a more intelligent table/map version
    int lastDot = fullName.lastIndexOf('.');
    if (lastDot <= 0) {
      throw new IllegalArgumentException("Stat name argument '" + fullName
          + "' not found");
    }
    int preLastDot = -1;
    String shortName = null;
    String ending = null;
    String ending2 = null;
    preLastDot = fullName.lastIndexOf('.', lastDot - 1);
    ending = fullName.substring(lastDot);
    if (preLastDot > 0) {
      shortName = fullName.substring(0, preLastDot);
      ending2 = fullName.substring(preLastDot);
    } else {
      shortName = fullName.substring(0, lastDot);
    }
    try {
      if (ending.equals(".60")) {
        if (ending2.equals(".sum.60")) {
          return counterMap.get(shortName).getMinuteSum();
        }
        if (ending2.equals(".avg.60")) {
          return counterMap.get(shortName).getMinuteAvg();
        }
        if (ending2.equals(".rate.60")) {
          return counterMap.get(shortName).getMinuteRate();
        }
        if (ending2.equals(".count.60")) {
          return counterMap.get(shortName).getMinuteSamples();
        }
      }
      if (ending.equals(".600")) {
        if (ending2.equals(".sum.600")) {
          return counterMap.get(shortName).getTenMinuteSum();
        }
        if (ending2.equals(".avg.600")) {
          return counterMap.get(shortName).getTenMinuteAvg();
        }
        if (ending2.equals(".rate.600")) {
          return counterMap.get(shortName).getTenMinuteRate();
        }
        if (ending2.equals(".count.600")) {
          return counterMap.get(shortName).getTenMinuteSamples();
        }
      }
      if (ending.equals(".3600")) {
        if (ending2.equals(".sum.3600")) {
          return counterMap.get(shortName).getHourSum();
        }
        if (ending2.equals(".avg.3600")) {
          return counterMap.get(shortName).getHourAvg();
        }
        if (ending2.equals(".rate.3600")) {
          return counterMap.get(shortName).getHourRate();
        }
        if (ending2.equals(".count.3600")) {
          return counterMap.get(shortName).getHourSamples();
        }
      }
      if (fullName.endsWith(".sum")) {
        return counterMap.get(shortName).getAllTimeSum();
      }
      if (fullName.endsWith(".avg")) {
        return counterMap.get(shortName).getAllTimeAvg();
      }
      if (fullName.endsWith(".rate")) {
        return counterMap.get(shortName).getAllTimeRate();
      }
      if (fullName.endsWith(".count")) {
        return counterMap.get(shortName).getAllTimeSamples();
      }
    } catch (Exception e) {
      throw new IllegalArgumentException("Stat name '" + shortName
          + "' not found for '" + ending2 + "' or '" + ending + "'");
    }
    throw new IllegalArgumentException("Stat name argument '" + fullName
        + "' not found");
  }
  /**
   * Returns a new map of results and any missing keys will be missing
   * from output map.
   *
   * fb303-support
   */
  public Map getSelectedCounters(List keys) {
    Map result = new HashMap();
    for (String key : keys) {
      try {
        result.put(key, getCounter(key));
      } catch (IllegalArgumentException e) {
        // okay, result will be missing for this
      }
    }
    return result;
  }
  /**
   * fb303-support
   *
   * @param fullName:  key with the .sum.60 parts, etc
   */
  public boolean hasCounter(String fullName) {
    try {
      getCounter(fullName);
      return true;
    } catch (IllegalArgumentException e) {
      return false;
    }
  }
  /**
   * Returns true if the shortName has been added already.
   *
   * @param shortName:  The stat name used with addStatExportType does
   *          NOT have the .sum.60 parts, etc
   */
  public boolean containsKey(String shortName) {
    return counterMap.containsKey(shortName);
  }
  /**
   * fb303-support
   */
  public Map getCounters()
  {
    Map result = new HashMap();
    String fullname;
    long value;
    Set typeKeys = typeMap.keySet();
    for (String name : typeKeys) {
      int bitmask = typeMap.get(name);
      MultiWindowGauge stat = counterMap.get(name);
      for (ExportType type : ExportType.values()) {
        if ((bitmask & type.value()) == 0) {
          continue;
        }
        // minute
        fullname = String.format("%s.%s.60", name, type);
        switch (type) {
        case SUM:
          value = stat.getMinuteSum();
          break;
        case COUNT:
          value = stat.getMinuteSamples();
          break;
        case AVG:
          value = stat.getMinuteAvg();
          break;
        case RATE:
          value = stat.getMinuteRate();
          break;
        default:
          throw new IndexOutOfBoundsException("Bad type");
        }
        result.put(fullname, value);
        // 10 min
        fullname = String.format("%s.%s.600", name, type);
        switch (type) {
        case SUM:
          value = stat.getTenMinuteSum();
          break;
        case COUNT:
          value = stat.getTenMinuteSamples();
          break;
        case AVG:
          value = stat.getTenMinuteAvg();
          break;
        case RATE:
          value = stat.getTenMinuteRate();
          break;
        default:
          throw new IndexOutOfBoundsException("Bad type");
        }
        result.put(fullname, value);
        // hour
        fullname = String.format("%s.%s.3600", name, type);
        switch (type) {
        case SUM:
          value = stat.getHourSum();
          break;
        case COUNT:
          value = stat.getHourSamples();
          break;
        case AVG:
          value = stat.getHourAvg();
          break;
        case RATE:
          value = stat.getHourRate();
          break;
        default:
          throw new IndexOutOfBoundsException("Bad type");
        }
        result.put(fullname, value);
        // all time
        fullname = String.format("%s.%s", name, type);
        switch (type) {
        case SUM:
          value = stat.getAllTimeSum();
          break;
        case COUNT:
          value = stat.getAllTimeSamples();
          break;
        case AVG:
          value = stat.getAllTimeAvg();
          break;
        case RATE:
          value = stat.getAllTimeRate();
          break;
        default:
          throw new IndexOutOfBoundsException("Bad type");
        }
        result.put(fullname, value);
      } // for all export types
    } // for all window stat names
    return result;
  }
}
                © 2015 - 2025 Weber Informatics LLC | Privacy Policy