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

com.newrelic.jfr.daemon.JFRUploader Maven / Gradle / Ivy

There is a newer version: 1.12.0
Show newest version
/*
 *
 *  * Copyright 2020 New Relic Corporation. All rights reserved.
 *  * SPDX-License-Identifier: Apache-2.0
 *
 */

package com.newrelic.jfr.daemon;

import static com.newrelic.jfr.daemon.AttributeNames.*;

import com.newrelic.telemetry.TelemetryClient;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Function;
import jdk.jfr.consumer.RecordingFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public final class JFRUploader {
  private static final Logger logger = LoggerFactory.getLogger(JFRUploader.class);

  public static final Function OPEN_RECORDING_FILE =
      file -> {
        try {
          return new RecordingFile(file);
        } catch (IOException e) {
          throw new RuntimeException("Error opening recording file", e);
        }
      };

  private final TelemetryClient telemetryClient;
  private final RecordedEventBuffer recordedEventBuffer;
  private final AtomicReference eventConverter;
  private final Function recordingFileOpener;
  private final Consumer fileDeleter;
  private final AtomicBoolean readinessCheck;

  private JFRUploader(Builder builder) {
    this.telemetryClient = builder.telemetryClient;
    this.recordedEventBuffer = builder.recordedEventBuffer;
    this.eventConverter = builder.eventConverter;
    this.recordingFileOpener = builder.recordingFileOpener;
    this.fileDeleter = builder.fileDeleter;
    this.readinessCheck = builder.readinessCheck;
  }

  void handleFile(final Path dumpFile) {
    try {
      bufferFileData(dumpFile);
      maybeDrainAndSend();
    } catch (Exception e) {
      logger.error("Error handling raw dump file", e);
    } finally {
      fileDeleter.accept(dumpFile);
    }
  }

  private void bufferFileData(Path dumpFile) {
    try (var recordingFile = recordingFileOpener.apply(dumpFile)) {
      recordedEventBuffer.bufferEvents(dumpFile, recordingFile);
    } catch (Throwable t) {
      logger.error("Error processing file " + dumpFile, t);
    }
  }

  private void maybeDrainAndSend() {
    if (!readinessCheck.get()) {
      logger.warn("Drain attempt skipped -- readiness check not yet ready.");
      return;
    }
    BufferedTelemetry telemetry = eventConverter.get().convert(recordedEventBuffer);
    sendMetrics(telemetry);
    sendEvents(telemetry);
  }

  private void sendMetrics(BufferedTelemetry bufferedMetrics) {
    var metricBatch = bufferedMetrics.createMetricBatch();
    if (!metricBatch.isEmpty()) {
      logger.info(String.format("Sending metric batch of size %s", metricBatch.size()));
      telemetryClient.sendBatch(metricBatch);
    }
  }

  private void sendEvents(BufferedTelemetry bufferedMetrics) {
    var eventBatch = bufferedMetrics.createEventBatch();
    if (!eventBatch.isEmpty()) {
      logger.info(String.format("Sending events batch of size %s", eventBatch.size()));
      telemetryClient.sendBatch(eventBatch);
    }
  }

  private static void deleteFile(Path dumpFile) {
    try {
      Files.delete(dumpFile);
    } catch (Exception e) {
      // TODO: I think we actually want to log an error here and exit cleanly, rather than
      // throw an exception on the executor thread
      throw new RuntimeException(e);
    }
  }

  public static Builder builder() {
    return new Builder();
  }

  public static class Builder {
    private TelemetryClient telemetryClient;
    private RecordedEventBuffer recordedEventBuffer;
    private AtomicReference eventConverter;
    private Function recordingFileOpener = OPEN_RECORDING_FILE;
    private Consumer fileDeleter = JFRUploader::deleteFile;
    private AtomicBoolean readinessCheck;

    public Builder telemetryClient(TelemetryClient telemetryClient) {
      this.telemetryClient = telemetryClient;
      return this;
    }

    public Builder recordedEventBuffer(RecordedEventBuffer recordedEventBuffer) {
      this.recordedEventBuffer = recordedEventBuffer;
      return this;
    }

    public Builder eventConverter(AtomicReference converter) {
      this.eventConverter = converter;
      return this;
    }

    public Builder recordingFileOpener(Function opener) {
      this.recordingFileOpener = opener;
      return this;
    }

    public Builder fileDeleter(Consumer fileDeleter) {
      this.fileDeleter = fileDeleter;
      return this;
    }

    public Builder readinessCheck(AtomicBoolean readinessCheck) {
      this.readinessCheck = readinessCheck;
      return this;
    }

    public JFRUploader build() {
      return new JFRUploader(this);
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy