We have an Elastic Search appender
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.AppenderBase;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.net.InetAddress;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.HttpHost;
import org.apache.http.impl.nio.reactor.IOReactorConfig;
import org.elasticsearch.ElasticsearchStatusException;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.common.xcontent.XContentType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ElasticSearchAppender extends AppenderBase<ILoggingEvent> {
private static final Logger log = LoggerFactory.getLogger(ElasticSearchAppender.class);
private static ObjectMapper mapper = new ObjectMapper();
private static RestHighLevelClient elastic;
private ExecutorService executorService = Executors.newFixedThreadPool(10);
private static RequestOptions COMMON_OPTIONS;
private String hostname;
private Integer port;
private String index;
private String application;
private String login;
private String password;
public ElasticSearchAppender() {
}
public String getLogin() {
return this.login;
}
public void setLogin(String login) {
this.login = login;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
this.password = password;
}
public String getApplication() {
return this.application;
}
public void setApplication(String application) {
this.application = application;
}
public String getHostname() {
return this.hostname;
}
public void setHostname(String hostname) {
this.hostname = hostname;
}
public Integer getPort() {
return this.port;
}
public void setPort(Integer port) {
this.port = port;
}
public String getIndex() {
return this.index;
}
public void setIndex(String index) {
this.index = index;
}
protected void append(ILoggingEvent event) {
if (elastic == null) {
this.createElasticConnection(this.hostname, this.port);
try {
CreateIndexRequest createIndexRequest = new CreateIndexRequest(this.index);
createIndexRequest.source(mapper.writeValueAsString(new Message(InetAddress.getLocalHost().getHostName(), LocalDateTime.ofInstant(Instant.ofEpochMilli(event.getTimeStamp()), ZoneOffset.UTC).toString(), event.getLevel().toString(), event.getLoggerName(), event.getMDCPropertyMap(), event.getFormattedMessage(), this.application)), XContentType.JSON);
elastic.indices().create(createIndexRequest, COMMON_OPTIONS);
} catch (ElasticsearchStatusException var3) {
log.error(var3.getMessage(), var3);
} catch (IOException var4) {
}
} else {
this.executorService.submit(() -> {
try {
IndexRequest indexRequest = new IndexRequest(this.index);
indexRequest.source(mapper.writeValueAsString(new Message(InetAddress.getLocalHost().getHostName(), LocalDateTime.ofInstant(Instant.ofEpochMilli(event.getTimeStamp()), ZoneOffset.UTC).toString(), event.getLevel().toString(), event.getLoggerName(), event.getMDCPropertyMap(), event.getFormattedMessage(), this.application)), XContentType.JSON);
elastic.index(indexRequest, COMMON_OPTIONS);
} catch (IOException var3) {
}
});
}
}
private void createElasticConnection(String hostname, int port) {
elastic = new RestHighLevelClient(RestClient.builder(new HttpHost[]{new HttpHost(hostname, port, "http")}).setRequestConfigCallback((requestConfigBuilder) -> {
return requestConfigBuilder.setConnectTimeout(5000).setSocketTimeout(60000);
}).setHttpClientConfigCallback((httpClientBuilder) -> {
return httpClientBuilder.setDefaultIOReactorConfig(IOReactorConfig.custom().setIoThreadCount(200).build());
}));
RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder();
builder.addHeader("Authorization", "Basic " + Base64.encodeBase64String((this.login + ":" + this.password).getBytes()));
COMMON_OPTIONS = builder.build();
}
private class Message {
private String hostname;
private String time;
private String level;
private String loggerName;
private Map<String, String> mdcProperty;
private String msg;
private String applicationName;
public String getHostname() {
return this.hostname;
}
public String getTime() {
return this.time;
}
public String getLevel() {
return this.level;
}
public String getLoggerName() {
return this.loggerName;
}
public Map<String, String> getMdcProperty() {
return this.mdcProperty;
}
public String getMsg() {
return this.msg;
}
public String getApplicationName() {
return this.applicationName;
}
public void setHostname(String hostname) {
this.hostname = hostname;
}
public void setTime(String time) {
this.time = time;
}
public void setLevel(String level) {
this.level = level;
}
public void setLoggerName(String loggerName) {
this.loggerName = loggerName;
}
public void setMdcProperty(Map<String, String> mdcProperty) {
this.mdcProperty = mdcProperty;
}
public void setMsg(String msg) {
this.msg = msg;
}
public void setApplicationName(String applicationName) {
this.applicationName = applicationName;
}
public Message() {
}
public Message(String hostname, String time, String level, String loggerName, Map<String, String> mdcProperty, String msg, String applicationName) {
this.hostname = hostname;
this.time = time;
this.level = level;
this.loggerName = loggerName;
this.mdcProperty = mdcProperty;
this.msg = msg;
this.applicationName = applicationName;
}
}
}
and when we have it in logback.xml
<appender name="Elastic" class="***.log.appender.ElasticSearchAppender">
<hostname>${elkHostname}</hostname>
<port>${elkPort}</port>
<index>${elkIndex}</index>
<application>${applicationName}</application>
<login>${login}</login>
<password>${password}</password>
</appender>
and if we enable
<logger name="org.apache.http" level="debug" additivity="false">
<appender-ref ref="STDOUT"/>
<appender-ref ref="Elastic"/>
</logger>
then we have our app hanging on the first http call caomint to it or initiated by the app according to our logs
It has been turned out, that Elastic initiates the call that should be itself logged by the Elastic appender due to org.apache.http
logger as elastic do the http calls. So, it turns into recursion and stackoverflow.
But in logs it looks just like nothing happens.
To work it around is to avoid using Elastic appender for the org.apache.http
logger.
<logger name="org.apache.http" level="debug" additivity="false">
<appender-ref ref="STDOUT"/>
<!--<appender-ref ref="Elastic"/>-->
</logger>