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

gobblin.metrics.InnerMetricContext Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2014-2016 LinkedIn Corp. 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.
 */

package gobblin.metrics;

import java.io.Closeable;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;

import com.codahale.metrics.Counter;
import com.codahale.metrics.Gauge;
import com.codahale.metrics.Histogram;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricFilter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.MetricSet;
import com.codahale.metrics.Timer;

import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Maps;
import com.google.common.io.Closer;

import lombok.Getter;

import gobblin.metrics.context.ContextWeakReference;
import gobblin.metrics.context.NameConflictException;
import gobblin.metrics.context.ReportableContext;
import gobblin.metrics.metric.InnerMetric;


/**
 * Contains list of {@link Metric}s, {@link Tag}s, as well as references to parent and child {@link MetricContext} for
 * a {@link MetricContext}. This object is only references by the corresponding {@link MetricContext} as well as the
 * {@link RootMetricContext}, and it is used to report metrics one last time after the corresponding {@link MetricContext}
 * has been GCed.
 */
public class InnerMetricContext extends MetricRegistry implements ReportableContext, Closeable {

  private final Closer closer;

  // Name of this context
  private final String name;

  // A map from simple names to context-aware metrics. All metrics registered with this context (using
  // their simple names) are also registered with the MetricRegistry using their fully-qualified names.
  private final ConcurrentMap contextAwareMetrics = Maps.newConcurrentMap();

  // This is used to work on tags associated with this context
  private final Tagged tagged;

  // Reference to the parent context wrapped in an Optional as there may be no parent context
  private final Optional parent;

  // A map from child context names to child contexts
  private final Cache children = CacheBuilder.newBuilder().weakValues().build();

  @Getter
  private final WeakReference metricContext;

  protected InnerMetricContext(MetricContext context, String name, MetricContext parent, List> tags)
      throws NameConflictException, ExecutionException {

    this.name = name;
    this.closer = Closer.create();
    this.parent = Optional.fromNullable(parent);
    if (this.parent.isPresent()) {
      this.parent.get().addChildContext(this.name, context);
      this.metricContext = new ContextWeakReference(context, this);
    } else {
      this.metricContext = new WeakReference<>(context);
    }
    this.tagged = new Tagged(tags);
    this.tagged.addTag(new Tag<>(MetricContext.METRIC_CONTEXT_ID_TAG_NAME, UUID.randomUUID().toString()));
    this.tagged.addTag(new Tag<>(MetricContext.METRIC_CONTEXT_NAME_TAG_NAME, name));
  }

  /**
   * Get the name of this {@link MetricContext}.
   *
   * @return the name of this {@link MetricContext}
   */
  @Override
  public String getName() {
    return this.name;
  }

  /**
   * Get the parent {@link MetricContext} of this {@link MetricContext} wrapped in an
   * {@link com.google.common.base.Optional}, which may be absent if it has not parent
   * {@link MetricContext}.
   *
   * @return the parent {@link MetricContext} of this {@link MetricContext} wrapped in an
   *         {@link com.google.common.base.Optional}
   */
  @Override
  public Optional getParent() {
    return this.parent;
  }

  /**
   * Add a {@link MetricContext} as a child of this {@link MetricContext} if it is not currently a child.
   *
   * @param childContextName the name of the child {@link MetricContext}
   * @param childContext the child {@link MetricContext} to add
   */
  public synchronized void addChildContext(String childContextName, final MetricContext childContext)
      throws NameConflictException, ExecutionException {
    if (this.children.get(childContextName, new Callable() {
      @Override
      public MetricContext call() throws Exception {
        return childContext;
      }
    }) != childContext) {
      throw new NameConflictException("A child context with that name already exists.");
    }
  }

  /**
   * Get a view of the child {@link gobblin.metrics.MetricContext}s as a {@link com.google.common.collect.ImmutableMap}.
   * @return {@link com.google.common.collect.ImmutableMap} of
   *      child {@link gobblin.metrics.MetricContext}s keyed by their names.
   */
  @Override
  public Map getChildContextsAsMap() {
    return ImmutableMap.copyOf(this.children.asMap());
  }

  /**
   * See {@link com.codahale.metrics.MetricRegistry#getNames()}.
   *
   * 

* This method will return fully-qualified metric names if the {@link MetricContext} is configured * to report fully-qualified metric names. *

*/ @Override public SortedSet getNames() { return getSimpleNames(); } /** * See {@link com.codahale.metrics.MetricRegistry#getMetrics()}. * *

* This method will return fully-qualified metric names if the {@link MetricContext} is configured * to report fully-qualified metric names. *

*/ @Override public Map getMetrics() { return getSimplyNamedMetrics(); } /** * See {@link com.codahale.metrics.MetricRegistry#getGauges(com.codahale.metrics.MetricFilter)}. * *

* This method will return fully-qualified metric names if the {@link MetricContext} is configured * to report fully-qualified metric names. *

*/ @Override public SortedMap getGauges(MetricFilter filter) { return getSimplyNamedMetrics(Gauge.class, Optional.of(filter)); } /** * See {@link com.codahale.metrics.MetricRegistry#getCounters(com.codahale.metrics.MetricFilter)}. * *

* This method will return fully-qualified metric names if the {@link MetricContext} is configured * to report fully-qualified metric names. *

*/ @Override public SortedMap getCounters(MetricFilter filter) { return getSimplyNamedMetrics(Counter.class, Optional.of(filter)); } /** * See {@link com.codahale.metrics.MetricRegistry#getHistograms(com.codahale.metrics.MetricFilter)}. * *

* This method will return fully-qualified metric names if the {@link MetricContext} is configured * to report fully-qualified metric names. *

*/ @Override public SortedMap getHistograms(MetricFilter filter) { return getSimplyNamedMetrics(Histogram.class, Optional.of(filter)); } /** * See {@link com.codahale.metrics.MetricRegistry#getMeters(com.codahale.metrics.MetricFilter)}. * *

* This method will return fully-qualified metric names if the {@link MetricContext} is configured * to report fully-qualified metric names. *

*/ @Override public SortedMap getMeters(MetricFilter filter) { return getSimplyNamedMetrics(Meter.class, Optional.of(filter)); } /** * See {@link com.codahale.metrics.MetricRegistry#getTimers(com.codahale.metrics.MetricFilter)}. * *

* This method will return fully-qualified metric names if the {@link MetricContext} is configured * to report fully-qualified metric names. *

*/ @Override public SortedMap getTimers(MetricFilter filter) { return getSimplyNamedMetrics(Timer.class, Optional.of(filter)); } /** * Register a given metric under a given name. * *

* This method does not support registering {@link com.codahale.metrics.MetricSet}s. * See{@link #registerAll(com.codahale.metrics.MetricSet)}. *

* *

* This method will not register a metric with the same name in the parent context (if it exists). *

*/ @Override public synchronized T register(String name, T metric) throws IllegalArgumentException { if (!(metric instanceof ContextAwareMetric)) { throw new UnsupportedOperationException("Can only register ContextAwareMetrics"); } if (this.contextAwareMetrics.putIfAbsent(name, ((ContextAwareMetric) metric).getInnerMetric()) != null) { throw new IllegalArgumentException("A metric named " + name + " already exists"); } MetricContext metricContext = this.metricContext.get(); if (metricContext != null) { metricContext.addToMetrics((ContextAwareMetric) metric); } // Also register the metric with the MetricRegistry using its fully-qualified name return metric; } /** * Register a {@link gobblin.metrics.ContextAwareMetric} under its own name. */ public T register(T metric) throws IllegalArgumentException { return register(metric.getName(), metric); } @Override public void registerAll(MetricSet metrics) throws IllegalArgumentException { throw new UnsupportedOperationException(); } /** * Remove a metric with a given name. * *

* This method will remove the metric with the given name from this {@link MetricContext} * as well as metrics with the same name from every child {@link MetricContext}s. *

* * @param name name of the metric to be removed * @return whether or not the metric has been removed */ @Override public synchronized boolean remove(String name) { MetricContext metricContext = this.metricContext.get(); if (metricContext != null) { metricContext.removeFromMetrics(this.contextAwareMetrics.get(name).getContextAwareMetric()); } return this.contextAwareMetrics.remove(name) != null && removeChildrenMetrics(name); } @Override public void removeMatching(MetricFilter filter) { for (Map.Entry entry : this.contextAwareMetrics.entrySet()) { if (filter.matches(entry.getKey(), entry.getValue().getContextAwareMetric())) { remove(entry.getKey()); } } } @Override public List> getTags() { return this.tagged.getTags(); } @Override public Map getTagMap() { return this.tagged.getTagMap(); } @Override public void close() throws IOException { this.closer.close(); } private SortedSet getSimpleNames() { return ImmutableSortedSet.copyOf(this.contextAwareMetrics.keySet()); } private Map getSimplyNamedMetrics() { return ImmutableMap. copyOf(this.contextAwareMetrics); } @SuppressWarnings("unchecked") private SortedMap getSimplyNamedMetrics(Class mClass, Optional filter) { ImmutableSortedMap.Builder builder = ImmutableSortedMap.naturalOrder(); for (Map.Entry entry : this.contextAwareMetrics.entrySet()) { if (mClass.isInstance(entry.getValue())) { if (filter.isPresent() && !filter.get().matches(entry.getKey(), entry.getValue().getContextAwareMetric())) { continue; } builder.put(entry.getKey(), (T) entry.getValue()); } } return builder.build(); } @SuppressWarnings("unchecked") protected synchronized T getOrCreate(String name, ContextAwareMetricFactory factory) { InnerMetric metric = this.contextAwareMetrics.get(name); if (metric != null) { if (factory.isInstance(metric)) { return (T) metric.getContextAwareMetric(); } throw new IllegalArgumentException(name + " is already used for a different type of metric"); } T newMetric = factory.newMetric(this.metricContext.get(), name); this.register(name, newMetric); return newMetric; } private boolean removeChildrenMetrics(String name) { boolean removed = true; for (MetricContext child : getChildContextsAsMap().values()) { if (!child.remove(name)) { removed = false; } } return removed; } @Override public String toString() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("InnerMetricContext Name: "); stringBuilder.append(this.name); if (this.getParent().isPresent()) { stringBuilder.append(", Parent Name: "); stringBuilder.append(this.getParent().get().getName()); } else { stringBuilder.append(", No Parent Context"); } stringBuilder.append(", Number of Children: "); stringBuilder.append(this.getChildContextsAsMap().size()); stringBuilder.append(", Tags: "); stringBuilder.append(Joiner.on(", ").withKeyValueSeparator(" : ").useForNull("NULL").join(this.getTagMap())); return stringBuilder.toString(); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy