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

org.jzenith.rest.metrics.MetricsInterceptor Maven / Gradle / Ivy

The newest version!
/**
 * Copyright © 2018 Marcus Thiesen ([email protected])
 *
 * 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 org.jzenith.rest.metrics;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Timer;
import lombok.NonNull;

import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.core.Request;
import java.io.IOException;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static java.util.Arrays.asList;

public class MetricsInterceptor implements ContainerRequestFilter, ContainerResponseFilter {

    private static final String START_TIME_NANOS_PROPERTY = "jzenith.requestStartTime.nanos";
    private static final LoadingCache STATUS_CODE_STRINGS = CacheBuilder.newBuilder()
            .build(new CacheLoader() {
                @Override
                public String load(Integer key) {
                    return key.toString();
                }
            });

    private static final Duration TIMER_MINIMUM_EXPECTED_DURATION = Duration.ofMillis(5);
    private static final Duration TIMER_MAXIMUM_EXPECTED_DURATION = Duration.ofSeconds(10);
    private static final Duration[] SLA_BUCKETS = {
            Duration.ofMillis(5),
            Duration.ofMillis(10),
            Duration.ofMillis(50),
            Duration.ofMillis(100),
            Duration.ofMillis(250),
            Duration.ofMillis(500),
            Duration.ofMillis(750),
            Duration.ofMillis(1000),
            Duration.ofMillis(2000),
            Duration.ofMillis(3000),
            Duration.ofMillis(4000),
            Duration.ofMillis(7500),
            Duration.ofMillis(10000)
    };

    private final String resourceName;
    private final String methodName;
    private final MeterRegistry registry;
    private final Tag resourceNameTag;
    private final Tag methodNameTag;

    public MetricsInterceptor(@NonNull final MeterRegistry registry, @NonNull final ResourceInfo resourceInfo) {
        resourceName = resourceInfo.getResourceClass().getSimpleName();
        methodName = resourceInfo.getResourceMethod().getName();
        this.registry = registry;
        resourceNameTag = Tag.of("resourceName", resourceName);
        methodNameTag = Tag.of("methodName", methodName);
    }

    private Timer.Builder timer() {
        return Timer.builder("rest.request.duration")
                .description("Request duration seconds")
                .minimumExpectedValue(TIMER_MINIMUM_EXPECTED_DURATION)
                .maximumExpectedValue(TIMER_MAXIMUM_EXPECTED_DURATION)
                .sla(SLA_BUCKETS);
    }

    @Override
    public void filter(@NonNull ContainerRequestContext requestContext) throws IOException {
        requestContext.setProperty(START_TIME_NANOS_PROPERTY, System.nanoTime());
    }

    @Override
    public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
        final Request request = requestContext.getRequest();
        final String statusString = STATUS_CODE_STRINGS.getUnchecked(responseContext.getStatus());

        final List tags = asList(resourceNameTag, methodNameTag,
                Tag.of("method", request.getMethod()), Tag.of("status", statusString));

        registry.counter("rest.request.total", tags).increment();

        final Long startTimeNano = (Long) requestContext.getProperty(START_TIME_NANOS_PROPERTY);

        timer().tags(tags).register(registry).record(System.nanoTime() - startTimeNano, TimeUnit.NANOSECONDS);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy