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

com.opentable.metrics.graphite.GraphiteConfiguration Maven / Gradle / Ivy

Go to download

Manages metrics and healthchecks and sends them to Graphite and exposes them via endpoints

There is a newer version: 6.0.1
Show newest version
/*
 * 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.opentable.metrics.graphite;

import java.time.Duration;
import java.util.Arrays;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

import javax.annotation.PreDestroy;

import com.codahale.metrics.MetricFilter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.MetricSet;
import com.codahale.metrics.ScheduledReporter;
import com.codahale.metrics.graphite.GraphiteReporter;
import com.codahale.metrics.graphite.GraphiteSender;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.env.Environment;

import com.opentable.service.AppInfo;
import com.opentable.service.EnvInfo;
import com.opentable.service.K8sInfo;
import com.opentable.service.ServiceInfo;
import com.opentable.spring.ConversionServiceConfiguration;

@Configuration
@Import({
        /**
         * {@link ConversionServiceConfiguration} needed for {@link java.time.Duration} config value.
         */
        ConversionServiceConfiguration.class,
        K8sInfo.class
})
/**
 * Spring-ey wrapper for Dropwizard Metrics Graphite reporter.
 *
 * 

* Constructs metric prefix based on {@link ServiceInfo}, {@link AppInfo}, and {@link EnvInfo}. Performs automatic * periodic Graphite failure detection and connection recycling. Provides {@link GraphiteSender} implementation * that bravely remains connected in face of danger. */ public class GraphiteConfiguration { static final String PREFIX = "metrics.graphite."; private static final Logger LOG = LoggerFactory.getLogger(GraphiteConfiguration.class); @Value("${ot.graphite.graphite-host:#{null}}") private String host; @Value("${ot.graphite.graphite-port:2003}") private int port; @Value("${ot.graphite.reporting-period:PT10s}") private Duration reportingPeriod; @Value("${ot.graphite.reporting.include.flavors:#{true}}") private boolean showFlavorInPrefix = true; @Value("ot.graphite.reporting.include.cluster.type:#{null}}") private String clusterNameType; private MetricRegistry metricRegistry; private MetricSet registeredMetrics; public Duration getReportingPeriod() { return reportingPeriod; } @Bean public ScheduledReporter graphiteReporter(Optional sender, MetricRegistry metricRegistry, ServiceInfo serviceInfo, AppInfo appInfo, K8sInfo k8sInfo, Environment environment) { if (!sender.isPresent()) { LOG.warn("No sender to report to, skipping reporter initialization"); return null; } final String prefix = getPrefix(serviceInfo, appInfo, k8sInfo, showFlavorInPrefix, ClusterNameType.fromParameterName(clusterNameType)); if (prefix == null) { LOG.warn("insufficient information to construct metric prefix; skipping reporter initialization"); return null; } LOG.info("initializing: host {}, port {}, prefix {}, refresh period {}", host, port, prefix, reportingPeriod); ScheduledReporter reporter; if (Boolean.parseBoolean(environment.getProperty("ot.graphite.reporter.legacy", "false"))) { LOG.debug("Using legacy graphite reporter"); reporter = GraphiteReporter.forRegistry(metricRegistry) .prefixedWith(prefix) .convertRatesTo(TimeUnit.SECONDS) .convertDurationsTo(TimeUnit.MILLISECONDS) .filter(MetricFilter.ALL) .build(sender.get()); } else { LOG.debug("Using new graphite reporter"); reporter = OtGraphiteReporter.forRegistry(metricRegistry) .prefixedWith(prefix) .convertRatesTo(TimeUnit.SECONDS) .convertDurationsTo(TimeUnit.MILLISECONDS) .filter(MetricFilter.ALL) .build(sender.get()); } reporter.start(reportingPeriod.toMillis(), TimeUnit.MILLISECONDS); return reporter; } @Bean public GraphiteSender graphiteSender(MetricRegistry metricRegistry) { this.metricRegistry = metricRegistry; if (!hasHost()) { LOG.info("no graphite host; skipping sender initialization"); return null; } final Graphite graphite = new Graphite(host, port); GraphiteSenderWrapper result = new GraphiteSenderWrapper(host, port, graphite); registeredMetrics = MetricSets.combineAndPrefix(PREFIX, result); metricRegistry.registerAll(registeredMetrics); return result; } @PreDestroy void close() { if (hasHost()) { MetricSets.removeAll(metricRegistry, registeredMetrics); } } @VisibleForTesting static String getPrefix(ServiceInfo serviceInfo, AppInfo appInfo, K8sInfo k8sInfo, boolean includeFlavorInPrefix, ClusterNameType clusterNameType ) { final String applicationName = serviceInfo.getName(); final EnvInfo env = appInfo.getEnvInfo(); final Integer i = appInfo.getInstanceNumber(); if (env.getType() == null || env.getLocation() == null || i == null) { return null; } final String name = env.getFlavor() == null || (!includeFlavorInPrefix) ? applicationName : applicationName + "-" + env.getFlavor(); final String instance = "instance-" + i; // On Kubernetes include the cluster name if (k8sInfo.isKubernetes() && k8sInfo.getClusterName().isPresent()) { switch(clusterNameType) { case SEPARATE: { return String.join(".", Arrays.asList("app_metrics", k8sInfo.getClusterName().get(), name, env.getType(), env.getLocation(), instance)); } case PART_OF_INSTANCE: { return String.join(".", Arrays.asList("app_metrics", name, env.getType(), env.getLocation(), k8sInfo.getClusterName().get() + "- "+ instance)); } case NONE: { break; // effectively skips } default: { throw new IllegalArgumentException("Can't understand ClusterNameType " + clusterNameType); } } } // Cluster name is not included return String.join(".", Arrays.asList("app_metrics", name, env.getType(), env.getLocation(), instance)); } private boolean hasHost() { return !Strings.isNullOrEmpty(host); } private enum ClusterNameType { PART_OF_INSTANCE("instance"), SEPARATE("separate"), NONE("none") ; private final String parameterName; ClusterNameType(String parameterName) { this.parameterName = parameterName; } public String getParameterName() { return parameterName; } public static ClusterNameType fromParameterName(String parameterName) { return Arrays.stream(ClusterNameType.values()) .filter(t -> t.getParameterName().equalsIgnoreCase(parameterName)) .findFirst().orElse(ClusterNameType.PART_OF_INSTANCE); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy