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

com.github.zhengframework.rest.metrics.MetricsFeature Maven / Gradle / Ivy

package com.github.zhengframework.rest.metrics;

import com.codahale.metrics.Counter;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import com.codahale.metrics.annotation.Counted;
import com.codahale.metrics.annotation.Metered;
import com.codahale.metrics.annotation.ResponseMetered;
import com.codahale.metrics.annotation.Timed;
import com.google.common.base.Joiner;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.ws.rs.ConstrainedTo;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.HEAD;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.OPTIONS;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.RuntimeType;
import javax.ws.rs.container.DynamicFeature;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.core.FeatureContext;
import javax.ws.rs.ext.Provider;

@Provider
@ConstrainedTo(RuntimeType.SERVER)
public class MetricsFeature implements DynamicFeature {

  private final MetricRegistry registry;
  private ConcurrentMap meters = new ConcurrentHashMap<>();
  private ConcurrentMap timers = new ConcurrentHashMap<>();
  private ConcurrentMap counters = new ConcurrentHashMap<>();
  private ConcurrentMap responseMeters = new ConcurrentHashMap<>();

  public MetricsFeature(MetricRegistry registry) {
    this.registry = registry;
  }

  @Override
  public void configure(ResourceInfo resourceInfo, FeatureContext context) {
    Method resourceMethod = resourceInfo.getResourceMethod();
    if (timers.containsKey(resourceMethod)) {
      context.register(timers.get(resourceMethod));
    } else {
      if (resourceMethod.isAnnotationPresent(Timed.class)) {
        final Timed annotation = resourceMethod.getAnnotation(Timed.class);
        final String name = chooseName(annotation.name(), annotation.absolute(), resourceInfo);
        final Timer timer = registry.timer(name);
        timers.putIfAbsent(resourceMethod, new TimedInterceptor(timer));
        context.register(timers.get(resourceMethod));
      }
    }

    if (meters.containsKey(resourceMethod)) {
      context.register(meters.get(resourceMethod));
    } else {
      if (resourceMethod.isAnnotationPresent(Metered.class)) {
        final Metered annotation = resourceMethod.getAnnotation(Metered.class);
        final String name = chooseName(annotation.name(), annotation.absolute(), resourceInfo);
        final Meter meter = registry.meter(name);
        meters.putIfAbsent(resourceMethod, new MeterInterceptor(meter));
        context.register(meters.get(resourceMethod));
      }
    }
    if (counters.containsKey(resourceMethod)) {
      context.register(counters.get(resourceMethod));
    } else {
      if (resourceMethod.isAnnotationPresent(Counted.class)) {
        Counted annotation = resourceMethod.getAnnotation(Counted.class);
        final String name = chooseName(annotation.name(), annotation.absolute(), resourceInfo);
        Counter counter = registry.counter(name);
        counters.putIfAbsent(resourceMethod, new CountedInterceptor(counter));
        context.register(counters.get(resourceMethod));
      }
    }

    if (responseMeters.containsKey(resourceMethod)) {
      context.register(responseMeters.get(resourceMethod));
    } else {
      if (resourceMethod.isAnnotationPresent(ResponseMetered.class)) {
        ResponseMetered annotation = resourceMethod.getAnnotation(ResponseMetered.class);
        final String name = chooseName(annotation.name(), annotation.absolute(), resourceInfo);
        List meters = Collections.unmodifiableList(Arrays.asList(
            registry.meter(MetricRegistry.name(name, "1xx-responses")), // 1xx
            registry.meter(MetricRegistry.name(name, "2xx-responses")), // 2xx
            registry.meter(MetricRegistry.name(name, "3xx-responses")), // 3xx
            registry.meter(MetricRegistry.name(name, "4xx-responses")), // 4xx
            registry.meter(MetricRegistry.name(name, "5xx-responses"))  // 5xx
        ));
        responseMeters.putIfAbsent(resourceMethod, new ResponseMeteredInterceptor(meters));
        context.register(responseMeters.get(resourceMethod));
      }
    }

  }

  private String chooseName(String explicitName, boolean absolute, ResourceInfo resourceInfo) {
    if (explicitName != null && !explicitName.isEmpty()) {
      if (absolute) {
        return explicitName;
      }
      return MetricRegistry.name(getName(resourceInfo), explicitName);
    }

    return getName(resourceInfo);
  }

  private String getName(ResourceInfo resourceInfo) {
    return getMethod(resourceInfo.getResourceMethod()) + " - " + getPath(resourceInfo);
  }

  private String getPath(ResourceInfo resourceInfo) {
    String rootPath = null;
    String methodPath = null;

    if (resourceInfo.getResourceClass().isAnnotationPresent(Path.class)) {
      rootPath = resourceInfo.getResourceClass().getAnnotation(Path.class).value();
    }

    if (resourceInfo.getResourceMethod().isAnnotationPresent(Path.class)) {
      methodPath = resourceInfo.getResourceMethod().getAnnotation(Path.class).value();
    }

    return Joiner.on("/").skipNulls().join(rootPath, methodPath);
  }

  private String getMethod(Method resourceMethod) {
    if (resourceMethod.isAnnotationPresent(GET.class)) {
      return HttpMethod.GET;
    }
    if (resourceMethod.isAnnotationPresent(POST.class)) {
      return HttpMethod.POST;
    }
    if (resourceMethod.isAnnotationPresent(PUT.class)) {
      return HttpMethod.PUT;
    }
    if (resourceMethod.isAnnotationPresent(DELETE.class)) {
      return HttpMethod.DELETE;
    }
    if (resourceMethod.isAnnotationPresent(HEAD.class)) {
      return HttpMethod.HEAD;
    }
    if (resourceMethod.isAnnotationPresent(OPTIONS.class)) {
      return HttpMethod.OPTIONS;
    }

    throw new IllegalStateException(
        "Resource method without GET, POST, PUT, DELETE, HEAD or OPTIONS annotation");
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy