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