001package org.avaje.metric.elastic;
002
003import okhttp3.MediaType;
004import okhttp3.OkHttpClient;
005import okhttp3.Request;
006import okhttp3.RequestBody;
007import okhttp3.Response;
008import org.avaje.metric.report.MetricReporter;
009import org.avaje.metric.report.ReportMetrics;
010import org.slf4j.Logger;
011import org.slf4j.LoggerFactory;
012
013import java.io.StringWriter;
014import java.net.ConnectException;
015import java.time.LocalDate;
016import java.util.concurrent.TimeUnit;
017
018/**
019 * Http(s) based Reporter that sends JSON formatted metrics directly to Elastic.
020 */
021public class ElasticHttpReporter implements MetricReporter {
022
023  private static final Logger logger = LoggerFactory.getLogger(ElasticHttpReporter.class);
024
025  private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
026
027  private final OkHttpClient client;
028
029  private final String bulkUrl;
030
031  private final ElasticReporterConfig config;
032
033  public ElasticHttpReporter(ElasticReporterConfig config) {
034    this.client = getClient(config);
035    this.config = config;
036    this.bulkUrl = config.getUrl() + "/_bulk";
037
038    // put the template to elastic if it is not already there
039    new TemplateApply(client, config.getUrl(), config.getTemplateName()).run();
040  }
041
042  private OkHttpClient getClient(ElasticReporterConfig config) {
043
044    OkHttpClient client = config.getClient();
045    if (client != null) {
046      return client;
047    } else {
048      return new OkHttpClient.Builder()
049          .connectTimeout(config.getConnectTimeout(), TimeUnit.SECONDS)
050          .readTimeout(config.getReadTimeout(), TimeUnit.SECONDS)
051          .writeTimeout(config.getWriteTimeout(), TimeUnit.SECONDS)
052          .build();
053    }
054  }
055
056  /**
057   * Send the non-empty metrics that were collected to the remote repository.
058   */
059  @Override
060  public void report(ReportMetrics reportMetrics) {
061
062    String today = today();
063    String json = null;
064    try {
065      StringWriter writer = new StringWriter(1000);
066      BulkJsonWriteVisitor jsonVisitor = new BulkJsonWriteVisitor(writer, reportMetrics, config, today);
067      jsonVisitor.write();
068
069      json = writer.toString();
070
071      if (logger.isTraceEnabled()) {
072        logger.trace("Sending:\n{}", json);
073      }
074
075      RequestBody body = RequestBody.create(JSON, json);
076      Request request = new Request.Builder()
077          .url(bulkUrl)
078          .post(body)
079          .build();
080
081      Response response = client.newCall(request).execute();
082      if (!response.isSuccessful()) {
083        logger.warn("Unsuccessful sending metrics payload to server - {}", response.body().string());
084        storeJsonForResend(json);
085      } else if (logger.isTraceEnabled()) {
086        logger.trace("Bulk Response - {}", response.body().string());
087      }
088
089    } catch (ConnectException e) {
090      logger.info("Connection error sending metrics to server: " + e.getMessage());
091      storeJsonForResend(json);
092
093    } catch (Exception e) {
094      logger.error("Unexpected error sending metrics to server", e);
095      storeJsonForResend(json);
096    }
097  }
098
099  private String today() {
100    return LocalDate.now().toString();
101  }
102
103  protected void storeJsonForResend(String json) {
104    // override this to support store and re-send 
105  }
106
107
108  @Override
109  public void cleanup() {
110    // Do nothing
111  }
112
113}