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}