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

com.palantir.conjure.java.okhttp.HostMetricsRegistry Maven / Gradle / Ivy

/*
 * (c) Copyright 2017 Palantir Technologies Inc. All rights reserved.
 *
 * 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.palantir.conjure.java.okhttp;

import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.common.base.Suppliers;
import com.palantir.logsafe.SafeArg;
import com.palantir.logsafe.UnsafeArg;
import com.palantir.logsafe.logger.SafeLogger;
import com.palantir.logsafe.logger.SafeLoggerFactory;
import java.time.Clock;
import java.time.Duration;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import org.immutables.value.Value;

public final class HostMetricsRegistry implements HostEventsSink {

    private static final SafeLogger log = SafeLoggerFactory.get(HostMetricsRegistry.class);

    private final LoadingCache hostMetrics;

    public HostMetricsRegistry() {
        this.hostMetrics = Caffeine.newBuilder()
                .maximumSize(1_000)
                .initialCapacity(64)
                .expireAfterAccess(Duration.ofDays(1))
                .build(key -> new DefaultHostMetrics(key.serviceName(), key.hostname(), key.port(), Clock.systemUTC()));
    }

    @Override
    public void record(String serviceName, String hostname, int port, int statusCode, long micros) {
        try {
            hostMetrics
                    .get(ImmutableServiceHostAndPort.of(serviceName, hostname, port))
                    .record(statusCode, micros);
        } catch (Exception e) {
            log.warn(
                    "Unable to record metrics for host and port",
                    UnsafeArg.of("hostname", hostname),
                    SafeArg.of("port", port),
                    e);
        }
    }

    @Override
    public void recordIoException(String serviceName, String hostname, int port) {
        try {
            hostMetrics
                    .get(ImmutableServiceHostAndPort.of(serviceName, hostname, port))
                    .recordIoException();
        } catch (Exception e) {
            log.warn(
                    "Unable to record IO exception for host and port",
                    UnsafeArg.of("hostname", hostname),
                    SafeArg.of("port", port),
                    e);
        }
    }

    @Override
    public HostEventCallback callback(String serviceName, String hostname, int port) {
        return new HostEventRegistryCallback(serviceName, hostname, port);
    }

    public Collection getMetrics() {
        return Collections.unmodifiableCollection(hostMetrics.asMap().values());
    }

    @Value.Immutable
    @ImmutablesStyle
    interface ServiceHostAndPort {
        @Value.Parameter
        String serviceName();

        @Value.Parameter
        String hostname();

        @Value.Parameter
        int port();
    }

    private final class HostEventRegistryCallback implements HostEventCallback {

        private final ServiceHostAndPort target;
        private final Supplier hostMetricsSupplier;

        HostEventRegistryCallback(String serviceName, String hostname, int port) {
            this.target = ImmutableServiceHostAndPort.of(serviceName, hostname, port);
            // recomputed each hour, must be shorter than the 1 day timeout used by the cache.
            // This avoids an unnecessary cache load to record metrics.
            this.hostMetricsSupplier =
                    Suppliers.memoizeWithExpiration(() -> hostMetrics.get(target), 1, TimeUnit.HOURS);
        }

        @Override
        public void record(int statusCode, long micros) {
            try {
                hostMetricsSupplier.get().record(statusCode, micros);
            } catch (Exception e) {
                log.warn(
                        "Unable to record metrics for host and port",
                        UnsafeArg.of("hostname", target.hostname()),
                        SafeArg.of("port", target.port()),
                        e);
            }
        }

        @Override
        public void recordIoException() {
            try {
                hostMetricsSupplier.get().recordIoException();
            } catch (Exception e) {
                log.warn(
                        "Unable to record IO exception for host and port",
                        UnsafeArg.of("hostname", target.hostname()),
                        SafeArg.of("port", target.port()),
                        e);
            }
        }

        @Override
        public String toString() {
            return "HostEventRegistryCallback{target=" + target + '}';
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy