All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.netflix.servo.monitor.DynamicTimer Maven / Gradle / Ivy

There is a newer version: 0.13.2
Show newest version
/*
 * Copyright 2011-2018 Netflix, 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.netflix.servo.monitor;

import com.netflix.servo.DefaultMonitorRegistry;
import com.netflix.servo.tag.TagList;
import com.netflix.servo.util.ExpiringCache;
import com.netflix.servo.util.Preconditions;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * Utility class that dynamically creates {@link BasicTimer}s based on an arbitrary
 * (name, tagList), or {@link MonitorConfig}. Timers are automatically expired after 15 minutes of
 * inactivity.
 */
public final class DynamicTimer extends AbstractMonitor
    implements CompositeMonitor, SpectatorMonitor {
  private static final String DEFAULT_EXPIRATION = "15";
  private static final String DEFAULT_EXPIRATION_UNIT = "MINUTES";
  private static final String CLASS_NAME = DynamicTimer.class.getCanonicalName();
  private static final String EXPIRATION_PROP = CLASS_NAME + ".expiration";
  private static final String EXPIRATION_PROP_UNIT = CLASS_NAME + ".expirationUnit";
  private static final String INTERNAL_ID = "servoTimers";
  private static final MonitorConfig BASE_CONFIG = new MonitorConfig.Builder(INTERNAL_ID).build();

  private static final DynamicTimer INSTANCE = new DynamicTimer();
  private final ExpiringCache timers;

  static class ConfigUnit {
    private final MonitorConfig config;
    private final TimeUnit unit;

    ConfigUnit(MonitorConfig config, TimeUnit unit) {
      this.config = config;
      this.unit = unit;
    }

    MonitorConfig getConfig() {
      return config;
    }

    TimeUnit getUnit() {
      return unit;
    }

    @Override
    public boolean equals(Object o) {
      if (this == o) {
        return true;
      }

      if (o == null || getClass() != o.getClass()) {
        return false;
      }

      final ConfigUnit that = (ConfigUnit) o;
      return config.equals(that.config) && unit == that.unit;
    }

    @Override
    public int hashCode() {
      int result = config.hashCode();
      result = 31 * result + unit.hashCode();
      return result;
    }
  }

  private DynamicTimer() {
    super(BASE_CONFIG);
    final String expiration = System.getProperty(EXPIRATION_PROP, DEFAULT_EXPIRATION);
    final String expirationUnit =
        System.getProperty(EXPIRATION_PROP_UNIT, DEFAULT_EXPIRATION_UNIT);
    final long expirationValue = Long.parseLong(expiration);
    final TimeUnit expirationUnitValue = TimeUnit.valueOf(expirationUnit);
    final long expireAfterMs = expirationUnitValue.toMillis(expirationValue);
    timers = new ExpiringCache<>(expireAfterMs, this::newTimer);
    DefaultMonitorRegistry.getInstance().register(this);
  }

  private BasicTimer newTimer(ConfigUnit configUnit) {
    return new BasicTimer(configUnit.config, configUnit.unit);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void initializeSpectator(TagList tags) {
  }

  private Timer get(MonitorConfig config, TimeUnit unit) {
    return timers.get(new ConfigUnit(config, unit));
  }

  /**
   * Returns a stopwatch that has been started and will automatically
   * record its result to the dynamic timer specified by the given config.
   *
   * @param config Config to identify a particular timer instance to update.
   * @param unit   The unit to use when reporting values to observers. For example if sent to
   *               a typical time series graphing system this would be the unit for the y-axis.
   *               It is generally recommended to use base units for reporting, so
   *               {@link TimeUnit#SECONDS} is the preferred value.
   */
  public static Stopwatch start(MonitorConfig config, TimeUnit unit) {
    return INSTANCE.get(config, unit).start();
  }

  /**
   * Returns a stopwatch that has been started and will automatically
   * record its result to the dynamic timer specified by the given config. The timer
   * will report the times in milliseconds to observers.
   *
   * @see #start(MonitorConfig, TimeUnit)
   */
  public static Stopwatch start(MonitorConfig config) {
    return INSTANCE.get(config, TimeUnit.MILLISECONDS).start();
  }

  /**
   * Record result to the dynamic timer indicated by the provided config
   * with a TimeUnit of milliseconds.
   */
  public static void record(MonitorConfig config, long duration) {
    INSTANCE.get(config, TimeUnit.MILLISECONDS).record(duration, TimeUnit.MILLISECONDS);
  }

  /**
   * Record a duration to the dynamic timer indicated by the provided config.
   * The units in which the timer is reported and the duration unit  are the same.
   *
   * @deprecated Use {@link DynamicTimer#record(MonitorConfig, java.util.concurrent.TimeUnit,
   * long, java.util.concurrent.TimeUnit)} instead.
   * The new method allows you to be specific about the units used for reporting the timer and
   * the units in which the duration is measured.
   */
  public static void record(MonitorConfig config, long duration, TimeUnit unit) {
    INSTANCE.get(config, unit).record(duration, unit);
  }

  /**
   * Record a duration to the dynamic timer indicated by the provided config/reportUnit.
   *
   * @param config       Config to identify a particular timer instance to update.
   * @param reportUnit   The unit to use when reporting values to observers. For example if sent to
   *                     a typical time series graphing system this would be the unit
   *                     for the y-axis.
   *                     It is generally recommended to use base units for reporting, so
   *                     {@link TimeUnit#SECONDS} is the preferred value.
   * @param duration     Measured duration to record.
   * @param durationUnit Unit for the measured duration. This should typically be the unit used for
   *                     timing source. For example if using {@link System#nanoTime()}
   *                     the unit would be nanoseconds.
   */
  public static void record(MonitorConfig config, TimeUnit reportUnit, long duration,
                            TimeUnit durationUnit) {
    INSTANCE.get(config, reportUnit).record(duration, durationUnit);
  }

  /**
   * Returns a stopwatch that has been started and will automatically
   * record its result to the dynamic timer specified by the given name, and sequence of (key,
   * value) pairs. The timer uses a TimeUnit of milliseconds.
   */
  public static Stopwatch start(String name, String... tags) {
    final MonitorConfig.Builder configBuilder = MonitorConfig.builder(name);
    Preconditions.checkArgument(tags.length % 2 == 0,
        "The sequence of (key, value) pairs must have even size: one key, one value");
    for (int i = 0; i < tags.length; i += 2) {
      configBuilder.withTag(tags[i], tags[i + 1]);
    }

    return INSTANCE.get(configBuilder.build(), TimeUnit.MILLISECONDS).start();
  }

  /**
   * Returns a stopwatch that has been started and will automatically
   * record its result to the dynamic timer specified by the given config. The timer
   * uses a TimeUnit of milliseconds.
   */
  public static Stopwatch start(String name, TagList list) {
    final MonitorConfig config = new MonitorConfig.Builder(name).withTags(list).build();
    return INSTANCE.get(config, TimeUnit.MILLISECONDS).start();
  }

  /**
   * Returns a stopwatch that has been started and will automatically
   * record its result to the dynamic timer specified by the given config. The timer
   * uses a TimeUnit of milliseconds.
   */
  public static Stopwatch start(String name, TagList list, TimeUnit unit) {
    final MonitorConfig config = new MonitorConfig.Builder(name).withTags(list).build();
    return INSTANCE.get(config, unit).start();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  @SuppressWarnings("unchecked")
  public List> getMonitors() {
    List list = timers.values();
    return (List>) list;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Long getValue(int pollerIdx) {
    return (long) timers.size();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public String toString() {
    return "DynamicCounter{baseConfig" + BASE_CONFIG
        + ", totalTimers=" + timers.size()
        + ", timers=" + timers + '}';
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy