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

org.apache.pinot.spi.metrics.PinotMetricUtils Maven / Gradle / Ivy

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.apache.pinot.spi.metrics;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import org.apache.pinot.spi.annotations.metrics.MetricsFactory;
import org.apache.pinot.spi.annotations.metrics.PinotMetricsFactory;
import org.apache.pinot.spi.env.PinotConfiguration;
import org.apache.pinot.spi.utils.PinotReflectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static org.apache.pinot.spi.utils.CommonConstants.CONFIG_OF_METRICS_FACTORY_CLASS_NAME;
import static org.apache.pinot.spi.utils.CommonConstants.DEFAULT_METRICS_FACTORY_CLASS_NAME;


public class PinotMetricUtils {
  private PinotMetricUtils() {
  }

  private static final Logger LOGGER = LoggerFactory.getLogger(PinotMetricUtils.class);
  private static final String METRICS_PACKAGE_REGEX_PATTERN = ".*\\.plugin\\.metrics\\..*";
  private static final Map METRICS_REGISTRY_MAP = new ConcurrentHashMap<>();
  private static final Map METRICS_REGISTRY_REGISTRATION_LISTENERS_MAP =
      new ConcurrentHashMap<>();

  private static PinotMetricsFactory _pinotMetricsFactory = null;

  /**
   * Initialize the metricsFactory ad registers the metricsRegistry
   */
  @VisibleForTesting
  public synchronized static void init(PinotConfiguration metricsConfiguration) {
    // Initializes PinotMetricsFactory.
    initializePinotMetricsFactory(metricsConfiguration);

    // Initializes metrics using the metrics configuration.
    initializeMetrics(metricsConfiguration);
    registerMetricsRegistry(getPinotMetricsRegistry());
  }

  /**
   * Initializes PinotMetricsFactory with metrics configurations.
   * @param metricsConfiguration The subset of the configuration containing the metrics-related keys
   */
  private static void initializePinotMetricsFactory(PinotConfiguration metricsConfiguration) {
    Set> classes = getPinotMetricsFactoryClasses();
    if (classes.size() > 1) {
      LOGGER.warn("More than one PinotMetricsFactory was found: {}", classes);
    }

    String metricsFactoryClassName = metricsConfiguration.getProperty(CONFIG_OF_METRICS_FACTORY_CLASS_NAME,
        DEFAULT_METRICS_FACTORY_CLASS_NAME);
    LOGGER.info("{} will be initialized as the PinotMetricsFactory", metricsFactoryClassName);

    Optional> clazzFound = classes.stream().filter(c -> c.getName().equals(metricsFactoryClassName))
        .findFirst();

    clazzFound.ifPresent(clazz -> {
          MetricsFactory annotation = clazz.getAnnotation(MetricsFactory.class);
          LOGGER.info("Trying to init PinotMetricsFactory: {} and MetricsFactory: {}", clazz, annotation);
          if (annotation.enabled()) {
            try {
              PinotMetricsFactory pinotMetricsFactory = (PinotMetricsFactory) clazz.newInstance();
              pinotMetricsFactory.init(metricsConfiguration);
              registerMetricsFactory(pinotMetricsFactory);
            } catch (Exception e) {
              LOGGER.error("Caught exception while initializing pinot metrics registry: {}, skipping it", clazz, e);
            }
          }
        }
    );

    Preconditions.checkState(_pinotMetricsFactory != null,
        "Failed to initialize PinotMetricsFactory. Please check if any pinot-metrics related jar is actually added to"
            + " the classpath.");
  }

  private static Set> getPinotMetricsFactoryClasses() {
    return PinotReflectionUtils.getClassesThroughReflection(METRICS_PACKAGE_REGEX_PATTERN, MetricsFactory.class);
  }

  /**
   * Initializes the metrics system by initializing the registry registration listeners present in the configuration.
   *
   * @param configuration The subset of the configuration containing the metrics-related keys
   */
  private static void initializeMetrics(PinotConfiguration configuration) {
    synchronized (PinotMetricUtils.class) {
      List listenerClassNames = configuration.getProperty("metricsRegistryRegistrationListeners",
          Arrays.asList(JmxReporterMetricsRegistryRegistrationListener.class.getName()));

      // Build each listener using their default constructor and add them
      for (String listenerClassName : listenerClassNames) {
        try {
          Class clazz =
              (Class) Class.forName(listenerClassName);
          Constructor defaultConstructor =
              clazz.getDeclaredConstructor();
          MetricsRegistryRegistrationListener listener = defaultConstructor.newInstance();

          LOGGER.info("Registering metricsRegistry to listener {}", listenerClassName);
          addMetricsRegistryRegistrationListener(listener);
        } catch (Exception e) {
          LOGGER
              .warn("Caught exception while initializing MetricsRegistryRegistrationListener " + listenerClassName, e);
        }
      }
    }
    LOGGER.info("Number of listeners got registered: {}", METRICS_REGISTRY_REGISTRATION_LISTENERS_MAP.size());
  }

  /**
   * Adds a metrics registry registration listener. When adding a metrics registry registration listener, events are
   * fired to add all previously registered metrics registries to the newly added metrics registry registration
   * listener.
   *
   * @param listener The listener to add
   */
  private static void addMetricsRegistryRegistrationListener(MetricsRegistryRegistrationListener listener) {
    synchronized (PinotMetricUtils.class) {
      METRICS_REGISTRY_REGISTRATION_LISTENERS_MAP.put(listener, Boolean.TRUE);

      // Fire events to register all previously registered metrics registries
      Set metricsRegistries = METRICS_REGISTRY_MAP.keySet();
      LOGGER.info("Number of metrics registry: {}", metricsRegistries.size());
      for (PinotMetricsRegistry metricsRegistry : metricsRegistries) {
        listener.onMetricsRegistryRegistered(metricsRegistry);
      }
    }
  }

  /**
   * Registers the metrics registry with the metrics helper.
   *
   * @param registry The registry to register
   */
  private static void registerMetricsRegistry(PinotMetricsRegistry registry) {
    synchronized (PinotMetricUtils.class) {
      METRICS_REGISTRY_MAP.put(registry, Boolean.TRUE);

      // Fire event to all registered listeners
      Set metricsRegistryRegistrationListeners =
          METRICS_REGISTRY_REGISTRATION_LISTENERS_MAP.keySet();
      for (MetricsRegistryRegistrationListener metricsRegistryRegistrationListener
          : metricsRegistryRegistrationListeners) {
        metricsRegistryRegistrationListener.onMetricsRegistryRegistered(registry);
      }
    }
  }

  /**
   * Registers an metrics factory.
   */
  private static void registerMetricsFactory(PinotMetricsFactory metricsFactory) {
    LOGGER.info("Registering metrics factory: {}", metricsFactory.getMetricsFactoryName());
    _pinotMetricsFactory = metricsFactory;
  }

  @VisibleForTesting
  public static PinotMetricsRegistry getPinotMetricsRegistry() {
    return getPinotMetricsRegistry(new PinotConfiguration(Collections.emptyMap()));
  }

  /**
   * Returns the metricsRegistry from the initialised metricsFactory.
   * If the metricsFactory is null, first creates and initializes the metricsFactory and registers the metrics registry.
   * @param metricsConfiguration metrics configs
   */
  public static synchronized PinotMetricsRegistry getPinotMetricsRegistry(PinotConfiguration metricsConfiguration) {
    if (_pinotMetricsFactory == null) {
      init(metricsConfiguration);
    }
    return _pinotMetricsFactory.getPinotMetricsRegistry();
  }

  public static PinotMetricName makePinotMetricName(Class klass, String name) {
    return _pinotMetricsFactory.makePinotMetricName(klass, name);
  }

  public static  PinotGauge makePinotGauge(Function condition) {
    return _pinotMetricsFactory.makePinotGauge(condition);
  }

  public static  PinotGauge makeGauge(PinotMetricsRegistry registry, PinotMetricName name, PinotGauge gauge) {
    return registry.newGauge(name, gauge);
  }

  public static PinotTimer makePinotTimer(PinotMetricsRegistry registry, PinotMetricName name, TimeUnit durationUnit,
      TimeUnit rateUnit) {
    return registry.newTimer(name, durationUnit, rateUnit);
  }

  public static PinotMeter makePinotMeter(PinotMetricsRegistry registry, PinotMetricName name, String eventType,
      TimeUnit unit) {
    return registry.newMeter(name, eventType, unit);
  }

  public static void removeMetric(PinotMetricsRegistry registry, PinotMetricName name) {
    registry.removeMetric(name);
  }

  public static PinotJmxReporter makePinotJmxReporter(PinotMetricsRegistry metricsRegistry) {
    return _pinotMetricsFactory.makePinotJmxReporter(metricsRegistry);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy