user1950349
user1950349

Reputation: 5146

How to reset metrics every X seconds?

I am trying to measure application and jvm level metrics on my application using DropWizard Metrics library.

Below is my metrics class which I am using across my code to increment/decrement the metrics. I am calling increment and decrement method of below class to increment and decrement metrics.

public class TestMetrics {
  private final MetricRegistry metricRegistry = new MetricRegistry();

  private static class Holder {
    private static final TestMetrics INSTANCE = new TestMetrics();
  }

  public static TestMetrics getInstance() {
    return Holder.INSTANCE;
  }

  private TestMetrics() {}

  public void increment(final Names... metricsName) {
    for (Names metricName : metricsName)
      metricRegistry.counter(name(TestMetrics.class, metricName.value())).inc();
  }

  public void decrement(final Names... metricsName) {
    for (Names metricName : metricsName)
      metricRegistry.counter(name(TestMetrics.class, metricName.value())).dec();
  }

  public MetricRegistry getMetricRegistry() {
    return metricRegistry;
  }

  public enum Names {
    // some more fields here
    INVALID_ID("invalid-id"), MESSAGE_DROPPED("drop-message");

    private final String value;

    private Names(String value) {
      this.value = value;
    }

    public String value() {
      return value;
    }
  };
}

And here is how I am using above TestMetrics class to increment the metrics basis on the case where I need to. Below method is called by multiple threads.

  public void process(GenericRecord record) {
    // ... some other code here
    try {
      String clientId = String.valueOf(record.get("clientId"));
      String procId = String.valueOf(record.get("procId"));
      if (Strings.isNullOrEmpty(clientId) && Strings.isNullOrEmpty(procId)
          && !NumberUtils.isNumber(clientId)) {
        TestMetrics.getInstance().increment(Names.INVALID_ID,
            Names.MESSAGE_DROPPED);
        return;
      }
      // .. other code here

    } catch (Exception ex) {    
      TestMetrics.getInstance().increment(Names.MESSAGE_DROPPED);
    }
  }

Now I have another class which runs every 30 seconds only (I am using Quartz framework for that) from where I want to print out all the metrics and its count. In general, I will send these metrics every 30 seconds to some other system but for now I am printing it out here. Below is how I am doing it.

public class SendMetrics implements Job {

  @Override
  public void execute(final JobExecutionContext ctx) throws JobExecutionException {
    MetricRegistry metricsRegistry = TestMetrics.getInstance().getMetricRegistry();
    Map<String, Counter> counters = metricsRegistry.getCounters();
    for (Entry<String, Counter> counter : counters.entrySet()) {
      System.out.println(counter.getKey());
      System.out.println(counter.getValue().getCount());
    }
  }
}

Now my question is: I want to reset all my metrics count every 30 seconds. Meaning when my execute method prints out the metrics, it should print out the metrics for that 30 second only (for all the metrics) instead of printing for that whole duration from when the program is running.

Is there any way that all my metrics should have count for 30 seconds only. Count of whatever has happened in last 30 seconds.

Upvotes: 2

Views: 5044

Answers (1)

zloster
zloster

Reputation: 1157

You want to reset the counters. There is no API for this. The reasons are discussed in GitHub issue JMX reset operation on metrics.

Workaround

Article Resettable counters with codahale metrics by EverTrue Engineering describes a possible workaround.

Step 1:

You have your counters and use them as usual - incrementing and decrementing. But you can't reset them. So add new Gauge which value is following the counter you want to reset after it have reported to you. The getValue() method of the Gauge is called when you want to report the counter value. After storing the current value the method is decreasing the value of the counter with it. This effectively reset the counter to 0. So you have your report and also have the counter reset.

metricsRegistry.register("my_resettable_gauge", new Gauge() {
    @Override
    public Long getValue() {
        Counter c = metrics.counter("my_counter_not_reported");
        long count = c.getCount();
        c.dec(count);
        return count;
    }
});

Step 2:

Add a filter that prohibits the actual counter to be reported because you are now reporting through the gauge.

GraphiteReporter.forRegistry(registry).filter(new MetricFilter() {
    @Override
    public boolean matches(String name, Metric metric) {
        return !name.contains("not_reported");
    }
}).build(client);

Upvotes: 1

Related Questions