gobblin.metrics.reporter.ContextAwareReporter Maven / Gradle / Ivy
                 Go to download
                
        
                    Show more of this group  Show more artifacts with this name
Show all versions of gobblin-metrics Show documentation
                Show all versions of gobblin-metrics Show documentation
Gobblin Ingestion Framework
                
            /*
 * 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.reporter;
import java.io.Closeable;
import java.io.IOException;
import java.util.Set;
import java.util.UUID;
import javax.annotation.Nullable;
import com.codahale.metrics.Reporter;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.typesafe.config.Config;
import lombok.extern.slf4j.Slf4j;
import gobblin.metrics.InnerMetricContext;
import gobblin.metrics.MetricContext;
import gobblin.metrics.RootMetricContext;
import gobblin.metrics.context.ReportableContext;
import gobblin.metrics.context.filter.ContextFilter;
import gobblin.metrics.context.filter.ContextFilterFactory;
import gobblin.metrics.notification.MetricContextCleanupNotification;
import gobblin.metrics.notification.NewMetricContextNotification;
import gobblin.metrics.notification.Notification;
/**
 * Base {@link Reporter} for gobblin metrics. Automatically handles {@link MetricContext} selection,
 * {@link gobblin.metrics.Metric} filtering, and changes to reporting on {@link MetricContext} life cycle.
 *
 * 
 *   The lifecycle of a {@link ContextAwareReporter} fully managed by the {@link RootMetricContext} is:
 *   {@code construct -> start -> stop -> close}. However, {@link ContextAwareReporter}s created manually by the user
 *   can have a different life cycle (for example  multiple calls to start / stop).
 * 
 */
@Slf4j
public class ContextAwareReporter implements Reporter, Closeable {
  private boolean started;
  private final UUID notificationTargetUUID;
  private final Set contextsToReport;
  private final ContextFilter contextFilter;
  protected final String name;
  protected final Config config;
  public ContextAwareReporter(String name, Config config) {
    this.name = name;
    this.config = config;
    this.started = false;
    RootMetricContext.get().addNewReporter(this);
    this.notificationTargetUUID = RootMetricContext.get().addNotificationTarget(new Function() {
      @Nullable @Override public Void apply(Notification input) {
        notificationCallback(input);
        return null;
      }
    });
    this.contextFilter = ContextFilterFactory.createContextFilter(config);
    this.contextsToReport = Sets.newConcurrentHashSet();
    for (MetricContext context : this.contextFilter.getMatchingContexts()) {
      this.contextsToReport.add(context.getInnerMetricContext());
    }
  }
  public boolean isStarted() {
    return this.started;
  }
  /**
   * Starts the {@link ContextAwareReporter}. If the {@link ContextAwareReporter} has been started
   * (and not stopped since), this is a no-op.
   */
  public final void start() {
    if (this.started) {
      log.warn(String.format("Reporter %s has already been started.", this.name));
      return;
    }
    try {
      startImpl();
      this.started = true;
    } catch (Exception exception) {
      log.warn(String.format("Reporter %s did not start correctly.", this.name), exception);
    }
  }
  /**
   * Actual logic for starting the {@link ContextAwareReporter}. This is a separate method from {@link #start()} to
   * allow {@link ContextAwareReporter} to handle the started / non-started state.
   */
  protected void startImpl() {
  }
  /**
   * Stops the {@link ContextAwareReporter}. If the {@link ContextAwareReporter} has not been started, or if it has been
   * stopped already, and not started since, this is a no-op.
   */
  public final void stop() {
    if (!this.started) {
      log.warn(String.format("Reporter %s has already been stopped.", this.name));
      return;
    }
    try {
      stopImpl();
      this.started = false;
    } catch (Exception exception) {
      log.warn(String.format("Reporter %s did not stop correctly.", this.name), exception);
    }
  }
  /**
   * Actual logic for stopping the {@link ContextAwareReporter}. This is a separate method from {@link #stop()} to
   * allow {@link ContextAwareReporter} to handle the started / non-started state.
   */
  protected void stopImpl() {
  }
  /**
   * Removes {@link ContextAwareReporter} records from the {@link RootMetricContext}.
   * This method should be considered irreversible and destructive to the {@link ContextAwareReporter}.
   * @throws IOException
   */
  @Override
  public void close() throws IOException {
    RootMetricContext.get().removeNotificationTarget(this.notificationTargetUUID);
    RootMetricContext.get().removeReporter(this);
  }
  /**
   * Callback used to receive notifications from the {@link RootMetricContext}.
   */
  private void notificationCallback(Notification notification) {
    if (notification instanceof MetricContextCleanupNotification) {
      removedMetricContext(((MetricContextCleanupNotification) notification).getMetricContext());
    }
    if (notification instanceof NewMetricContextNotification) {
      newMetricContext(((NewMetricContextNotification) notification).getMetricContext());
    }
  }
  /**
   * Called when any {@link MetricContext} is removed from the tree.
   *
   * @param context {@link InnerMetricContext} backing the removed {@link MetricContext}.
   */
  protected void removedMetricContext(InnerMetricContext context) {
    this.contextsToReport.remove(context);
    if (context.getParent().isPresent() && this.contextFilter.shouldReplaceByParent(context)) {
      this.contextsToReport.add(context.getParent().get().getInnerMetricContext());
    }
  }
  /**
   * Called whenever a new {@link MetricContext} is added to the tree.
   *
   * @param context new {@link MetricContext} added.
   */
  protected void newMetricContext(MetricContext context) {
    if (this.contextFilter.matches(context)) {
      this.contextsToReport.add(context.getInnerMetricContext());
    }
  }
  /**
   * Whether a {@link InnerMetricContext} should be reported. Called when a {@link MetricContext} has been removed and
   * just before the corresponding {@link InnerMetricContext} is removed.
   */
  protected boolean shouldReportInnerMetricContext(InnerMetricContext context) {
    return this.contextsToReport.contains(context);
  }
  /**
   * @return an {@link Iterable} of all {@link MetricContext}s to report.
   */
  protected Iterable getMetricContextsToReport() {
    return ImmutableSet.copyOf(this.contextsToReport);
  }
}
        © 2015 - 2025 Weber Informatics LLC | Privacy Policy