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

com.newrelic.telemetry.micrometer.NewRelicRegistry Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2020 New Relic Corporation. All rights reserved.
 * SPDX-License-Identifier: Apache-2.0
 */

package com.newrelic.telemetry.micrometer;

import com.newrelic.telemetry.Attributes;
import com.newrelic.telemetry.SenderConfiguration;
import com.newrelic.telemetry.TelemetryClient;
import com.newrelic.telemetry.metrics.Metric;
import com.newrelic.telemetry.metrics.MetricBatch;
import com.newrelic.telemetry.metrics.MetricBatchSender;
import com.newrelic.telemetry.micrometer.transform.AttributesMaker;
import com.newrelic.telemetry.micrometer.transform.BareMeterTransformer;
import com.newrelic.telemetry.micrometer.transform.CommonCounterTransformer;
import com.newrelic.telemetry.micrometer.transform.CounterAdapter;
import com.newrelic.telemetry.micrometer.transform.DistributionSummaryTransformer;
import com.newrelic.telemetry.micrometer.transform.FunctionCounterAdapter;
import com.newrelic.telemetry.micrometer.transform.FunctionTimerTransformer;
import com.newrelic.telemetry.micrometer.transform.GaugeTransformer;
import com.newrelic.telemetry.micrometer.transform.HistogramGaugeCustomizer;
import com.newrelic.telemetry.micrometer.transform.LongTaskTimerTransformer;
import com.newrelic.telemetry.micrometer.transform.TimeGaugeTransformer;
import com.newrelic.telemetry.micrometer.transform.TimerTransformer;
import com.newrelic.telemetry.micrometer.util.TimeTracker;
import io.micrometer.core.instrument.Clock;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.DistributionSummary;
import io.micrometer.core.instrument.FunctionCounter;
import io.micrometer.core.instrument.FunctionTimer;
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.LongTaskTimer;
import io.micrometer.core.instrument.Meter;
import io.micrometer.core.instrument.TimeGauge;
import io.micrometer.core.instrument.Timer;
import io.micrometer.core.instrument.distribution.DistributionStatisticConfig;
import io.micrometer.core.instrument.distribution.pause.PauseDetector;
import io.micrometer.core.instrument.step.StepDistributionSummary;
import io.micrometer.core.instrument.step.StepMeterRegistry;
import io.micrometer.core.instrument.step.StepTimer;
import io.micrometer.core.instrument.util.MeterPartition;
import io.micrometer.core.ipc.http.HttpSender;
import io.micrometer.core.ipc.http.HttpUrlConnectionSender;
import java.net.MalformedURLException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class NewRelicRegistry extends StepMeterRegistry {

  private static final Logger LOG = LoggerFactory.getLogger(NewRelicRegistry.class);
  private static final String implementationVersion;

  private final NewRelicRegistryConfig config;
  private final TelemetryClient telemetryClient;
  private final Attributes commonAttributes;
  private final TimeGaugeTransformer timeGaugeTransformer;
  private final GaugeTransformer gaugeTransformer;
  private final TimerTransformer timerTransformer;

  private final FunctionTimerTransformer functionTimerTransformer;
  private final CommonCounterTransformer counterTransformer;
  private final LongTaskTimerTransformer longTaskTimerTransformer;
  private final CommonCounterTransformer functionCounterTransformer;
  private final DistributionSummaryTransformer distributionSummaryTransformer;
  private final BareMeterTransformer bareMeterTransformer;
  private final TimeTracker timeTracker;
  private final HistogramGaugeCustomizer histogramCustomizer;

  static {
    Package thisPackage = NewRelicRegistry.class.getPackage();
    implementationVersion =
        Optional.ofNullable(thisPackage.getImplementationVersion()).orElse("UnknownVersion");
  }

  // visible for testing
  private NewRelicRegistry(
      NewRelicRegistryConfig config,
      Clock clock,
      Attributes commonAttributes,
      AttributesMaker attributesMaker,
      TimeTracker timeTracker,
      MetricBatchSender metricBatchSender) {
    this(
        config,
        clock,
        commonAttributes,
        new TelemetryClient(metricBatchSender, null, null, null),
        new TimeGaugeTransformer(new GaugeTransformer(clock, attributesMaker)),
        new GaugeTransformer(clock, attributesMaker),
        new TimerTransformer(timeTracker),
        new FunctionTimerTransformer(timeTracker),
        new CommonCounterTransformer<>(timeTracker, attributesMaker, CounterAdapter::new),
        new LongTaskTimerTransformer(clock),
        new CommonCounterTransformer<>(timeTracker, attributesMaker, FunctionCounterAdapter::new),
        new DistributionSummaryTransformer(timeTracker, attributesMaker),
        new BareMeterTransformer(clock),
        new HistogramGaugeCustomizer(),
        timeTracker);
  }

  NewRelicRegistry(
      NewRelicRegistryConfig config,
      Clock clock,
      Attributes commonAttributes,
      TelemetryClient telemetryClient,
      TimeGaugeTransformer timeGaugeTransformer,
      GaugeTransformer gaugeTransformer,
      TimerTransformer timerTransformer,
      FunctionTimerTransformer functionTimerTransformer,
      CommonCounterTransformer counterTransformer,
      LongTaskTimerTransformer longTaskTimerTransformer,
      CommonCounterTransformer functionCounterTransformer,
      DistributionSummaryTransformer distributionSummaryTransformer,
      BareMeterTransformer bareMeterTransformer,
      HistogramGaugeCustomizer histogramCustomizer,
      TimeTracker timeTracker) {
    super(config, clock);
    this.config = config;
    this.commonAttributes =
        commonAttributes
            .copy()
            .put("instrumentation.provider", "micrometer")
            .put("collector.name", "micrometer-registry-newrelic")
            .put("collector.version", implementationVersion);
    if (config.serviceName() != null) {
      this.commonAttributes.put("service.name", config.serviceName());
    }
    this.telemetryClient = telemetryClient;
    this.timeGaugeTransformer = timeGaugeTransformer;
    this.gaugeTransformer = gaugeTransformer;
    this.timerTransformer = timerTransformer;
    this.functionTimerTransformer = functionTimerTransformer;
    this.counterTransformer = counterTransformer;
    this.longTaskTimerTransformer = longTaskTimerTransformer;
    this.functionCounterTransformer = functionCounterTransformer;
    this.distributionSummaryTransformer = distributionSummaryTransformer;
    this.bareMeterTransformer = bareMeterTransformer;
    this.timeTracker = timeTracker;
    this.histogramCustomizer = histogramCustomizer;
  }

  @Override
  public void start(ThreadFactory threadFactory) {
    LOG.info("New Relic Registry: Version " + implementationVersion + " is starting");
    super.start(threadFactory);
  }

  @Override
  public void close() {
    super.close();
    // NOTE: telemetryClient.shutdown is called after calling "close"
    // so that we can flush the last metricBatch
    this.telemetryClient.shutdown();
  }

  @Override
  protected void publish() {
    List> partitionedData = MeterPartition.partition(this, config.batchSize());
    for (List batch : partitionedData) {
      List metrics = new ArrayList<>();
      batch.forEach(
          meter -> {
            if (meter instanceof TimeGauge) {
              metrics.add(timeGaugeTransformer.transform((TimeGauge) meter));
            } else if (meter instanceof Gauge) {
              metrics.add(gaugeTransformer.transform((Gauge) meter));
            } else if (meter instanceof Timer) {
              metrics.addAll(timerTransformer.transform((Timer) meter));
            } else if (meter instanceof FunctionTimer) {
              metrics.addAll(functionTimerTransformer.transform((FunctionTimer) meter));
            } else if (meter instanceof Counter) {
              metrics.add(counterTransformer.transform((Counter) meter));
            } else if (meter instanceof DistributionSummary) {
              metrics.addAll(distributionSummaryTransformer.transform((DistributionSummary) meter));
            } else if (meter instanceof LongTaskTimer) {
              metrics.addAll(longTaskTimerTransformer.transform((LongTaskTimer) meter));
            } else if (meter instanceof FunctionCounter) {
              metrics.add(functionCounterTransformer.transform((FunctionCounter) meter));
            } else {
              metrics.addAll(bareMeterTransformer.transform(meter));
            }
          });
      telemetryClient.sendBatch(new MetricBatch(metrics, commonAttributes));
    }
    timeTracker.tick();
  }

  @Override
  protected TimeUnit getBaseTimeUnit() {
    return TimeUnit.MILLISECONDS;
  }

  public static NewRelicRegistryBuilder builder(NewRelicRegistryConfig config) {
    return new NewRelicRegistryBuilder(config);
  }

  @Override
  protected Timer newTimer(
      Meter.Id id,
      DistributionStatisticConfig distributionStatisticConfig,
      PauseDetector pauseDetector) {
    Timer timer =
        new StepTimer(
            id,
            clock,
            distributionStatisticConfig,
            pauseDetector,
            getBaseTimeUnit(),
            this.config.step().toMillis(),
            false);
    histogramCustomizer.registerHistogramGauges(timer, this);
    return timer;
  }

  @Override
  protected DistributionSummary newDistributionSummary(
      Meter.Id id, DistributionStatisticConfig distributionStatisticConfig, double scale) {
    DistributionSummary summary =
        new StepDistributionSummary(
            id, clock, distributionStatisticConfig, scale, config.step().toMillis(), false);
    histogramCustomizer.registerHistogramGauges(summary, this);
    return summary;
  }

  public static class NewRelicRegistryBuilder {

    private final NewRelicRegistryConfig config;
    private HttpSender httpSender = new HttpUrlConnectionSender();
    private Attributes commonAttributes = new Attributes();

    public NewRelicRegistryBuilder(NewRelicRegistryConfig config) {
      this.config = config;
    }

    public NewRelicRegistryBuilder httpSender(HttpSender httpSender) {
      this.httpSender = httpSender;
      return this;
    }

    /**
     * Supply a set of attributes that should be applied to all metrics.
     *
     * @param commonAttributes The attributes that relate to all metrics
     * @return {@literal this}
     */
    public NewRelicRegistryBuilder commonAttributes(Attributes commonAttributes) {
      this.commonAttributes = commonAttributes;
      return this;
    }

    public NewRelicRegistry build() {
      MetricBatchSender metricBatchSender = createMetricBatchSender();
      return new NewRelicRegistry(
          config,
          Clock.SYSTEM,
          commonAttributes,
          new AttributesMaker(),
          new TimeTracker(Clock.SYSTEM),
          metricBatchSender);
    }

    private MetricBatchSender createMetricBatchSender() {
      SenderConfiguration.SenderConfigurationBuilder builder =
          MetricBatchSender.configurationBuilder()
              .apiKey(config.apiKey())
              .useLicenseKey(config.useLicenseKey())
              .httpPoster(new MicrometerHttpPoster(httpSender))
              .secondaryUserAgent("NewRelic-Micrometer-Exporter/" + implementationVersion)
              .auditLoggingEnabled(config.enableAuditMode());
      builder = configureEndpoint(builder);
      return MetricBatchSender.create(builder.build());
    }

    private SenderConfiguration.SenderConfigurationBuilder configureEndpoint(
        SenderConfiguration.SenderConfigurationBuilder builder) {
      if (config.uri() == null) {
        return builder;
      }
      try {
        URI uri = URI.create(config.uri());
        return builder.endpoint(uri.toURL());
      } catch (MalformedURLException e) {
        throw new RuntimeException("Invalid URI for the metric API : " + config.uri(), e);
      }
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy