content: /*
   * 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.cassandra.sidecar.metrics;
  
  import java.util.Collections;
  import java.util.List;
  
  import com.google.common.annotations.VisibleForTesting;
  
  import com.codahale.metrics.MetricRegistry;
  import com.codahale.metrics.SharedMetricRegistries;
  import org.apache.cassandra.sidecar.config.SidecarConfiguration;
  
  /**
   * Provider for getting {@link FilteringMetricRegistry} based on provided metrics configuration
   */
  public class MetricRegistryFactory
  {
      private final String globalRegistryName;
      private final List<MetricFilter> inclusions;
      private final List<MetricFilter> exclusions;
  
      public MetricRegistryFactory(SidecarConfiguration sidecarConfiguration)
      {
          this(sidecarConfiguration.metricsConfiguration().registryName(),
               MetricFilter.parse(sidecarConfiguration.metricsConfiguration().includeConfigurations()),
               MetricFilter.parse(sidecarConfiguration.metricsConfiguration().excludeConfigurations()));
      }
  
      @VisibleForTesting
      public MetricRegistryFactory(String globalRegistryName, List<MetricFilter> inclusions, List<MetricFilter> exclusions)
      {
          this.globalRegistryName = globalRegistryName;
          this.inclusions = Collections.unmodifiableList(inclusions);
          this.exclusions = Collections.unmodifiableList(exclusions);
      }
  
      public MetricRegistry getOrCreate()
      {
          return getOrCreate(globalRegistryName);
      }
  
      public MetricRegistry getOrCreate(int cassInstanceId)
      {
          String instanceRegistryName = globalRegistryName + "_" + cassInstanceId;
          return getOrCreate(instanceRegistryName);
      }
  
      /**
       * Provides a {@link FilteringMetricRegistry} with given name and provided filters in configuration.
       * @param name registry name
       * @return a {@link MetricRegistry} that can filter out metrics
       */
      public MetricRegistry getOrCreate(String name)
      {
          // the metric registry already exists
          if (SharedMetricRegistries.names().contains(name))
          {
              return SharedMetricRegistries.getOrCreate(name);
          }
  
          FilteringMetricRegistry metricRegistry = new FilteringMetricRegistry(this::isAllowed);
          SharedMetricRegistries.add(name, metricRegistry);
          return SharedMetricRegistries.getOrCreate(name);
      }
  
      /**
       * Check if the metric is allowed to register
       * The evaluation order is inclusions first, then exclusions. In other words,
       * a metric name is allowed if it is in the inclusions, but not in the exclusions.
       * <p>
       * Note that an empty inclusions means including all
       *
       * @param name  metric name
       * @return true if allowed; false otherwise
       */
      private boolean isAllowed(String name)
      {
          boolean included = inclusions.isEmpty() || inclusions.stream().anyMatch(filter -> filter.matches(name));
          boolean excluded = exclusions.stream().anyMatch(filter -> filter.matches(name));
          return included && !excluded;
      }
  }
  