Skip to content
This repository has been archived by the owner on Jun 29, 2023. It is now read-only.

Added GelfHTTPSender #68

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
<logback-classic.version>1.1.3</logback-classic.version>
<org.jboss.logmanager.version>1.5.2.Final</org.jboss.logmanager.version>
<arquillian.version>1.1.5.Final</arquillian.version>
<http-components.version>4.5.1</http-components.version>

<site-plugin.version>3.4</site-plugin.version>
<maven-javadoc-plugin.version>2.7</maven-javadoc-plugin.version>
Expand Down Expand Up @@ -287,6 +288,14 @@
<scope>provided</scope>
</dependency>

<!-- HTTP Logging -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${http-components.version}</version>
</dependency>


<!-- Test -->
<dependency>
<groupId>junit</groupId>
Expand Down Expand Up @@ -360,6 +369,7 @@
<artifactId>shrinkwrap-descriptors-impl-javaee</artifactId>
<scope>test</scope>
</dependency>

</dependencies>

<reporting>
Expand Down
71 changes: 68 additions & 3 deletions src/main/java/biz/paluch/logging/gelf/GelfUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

import biz.paluch.logging.gelf.intern.GelfMessage;

import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* @author <a href="mailto:[email protected]">Mark Paluch</a>
Expand Down Expand Up @@ -93,4 +95,67 @@ public static Set<String> getMatchingMdcNames(DynamicMdcMessageField field, Set<
}
return matchingMdcNames;
}

public static String addDefaultPortIfMissing(String urlString, String defaultPort) {
URL url;
try {
url = new URL(urlString);
} catch (MalformedURLException e) {
return urlString;
}
if (url.getPort() != -1) {
return urlString;
}
String regex = "http://([^/]+)";
String found = getFirstFound(urlString, regex);
String replacer = "http://" + found + ":" + defaultPort;

if (!isEmpty(found)) {
urlString = urlString.replaceFirst(regex, replacer);
}
return urlString;
}

public static String getFirstFound(String contents, String regex) {
List<String> founds = getFound(contents, regex);
if (isEmpty(founds)) {
return null;
}
return founds.get(0);
}

public static List<String> getFound(String contents, String regex) {
if (isEmpty(regex) || isEmpty(contents)) {
return null;
}
List<String> results = new ArrayList<String>();
Pattern pattern = Pattern.compile(regex, Pattern.UNICODE_CASE);
Matcher matcher = pattern.matcher(contents);

while (matcher.find()) {
if (matcher.groupCount() > 0) {
results.add(matcher.group(1));
} else {
results.add(matcher.group());
}
}
return results;
}

public static boolean isEmpty(List<String> list) {
if (list == null || list.size() == 0) {
return true;
}
if (list.size() == 1 && isEmpty(list.get(0))) {
return true;
}
return false;
}

public static boolean isEmpty(String str) {
if (str != null && str.trim().length() > 0) {
return false;
}
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
import biz.paluch.logging.gelf.intern.GelfSender;
import biz.paluch.logging.gelf.intern.GelfSenderConfiguration;
import biz.paluch.logging.gelf.intern.GelfSenderProvider;
import org.apache.http.impl.client.HttpClientBuilder;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

/**
*
* (c) https://github.com/Batigoal/logstash-gelf.git
*
*/
public class DefaultGelfSenderProvider implements GelfSenderProvider {

Expand All @@ -31,13 +30,15 @@ public GelfSender create(GelfSenderConfiguration configuration) throws IOExcepti
}

if (graylogHost.startsWith("tcp:")) {

int timeoutMs = (int) TimeUnit.MILLISECONDS.convert(2, TimeUnit.SECONDS);
String tcpGraylogHost = graylogHost.substring(4, graylogHost.length());
return new GelfTCPSender(tcpGraylogHost, port, timeoutMs, timeoutMs, configuration.getErrorReporter());
} else if (graylogHost.startsWith("udp:")) {
String udpGraylogHost = graylogHost.substring(4, graylogHost.length());
return new GelfUDPSender(udpGraylogHost, port, configuration.getErrorReporter());
} else if (graylogHost.startsWith("http")) {
return new GelfHTTPSender(graylogHost, port, configuration.getErrorReporter(), HttpClientBuilder.create());

} else {
return new GelfUDPSender(graylogHost, port, configuration.getErrorReporter());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package biz.paluch.logging.gelf.intern.sender;

import biz.paluch.logging.gelf.intern.Closer;
import biz.paluch.logging.gelf.intern.ErrorReporter;
import biz.paluch.logging.gelf.intern.GelfMessage;
import biz.paluch.logging.gelf.intern.GelfSender;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;

import static biz.paluch.logging.gelf.GelfUtil.addDefaultPortIfMissing;

/**
* Created by https://github.com/salex89
*/
public class GelfHTTPSender implements GelfSender {

final private String uri;
final private int port;
final private ErrorReporter errorReporter;
final private CloseableHttpClient httpClient;


public GelfHTTPSender(String uri, int port, ErrorReporter errorReporter, HttpClientBuilder builder) {

this.uri = addDefaultPortIfMissing(uri, String.valueOf(port));
this.port = port;
this.errorReporter = errorReporter;


httpClient = builder.build();
}

@Override
public boolean sendMessage(GelfMessage message) {

CloseableHttpResponse httpResponse = null;
try {
HttpPost post = new HttpPost(uri);
String messageJson = message.toJson();
post.setEntity(new StringEntity(messageJson));
httpResponse = httpClient.execute(post);
int responseStatusCode = httpResponse.getStatusLine().getStatusCode();
if (responseStatusCode == 202) {
return true;
} else {
errorReporter.reportError("HTTP responded with non-202 status code: " +
responseStatusCode, new IOException("Cannot send data to " + uri + ":" + port));
}
} catch (UnsupportedEncodingException e) {
errorReporter.reportError(e.getMessage(), new IOException("Cannot create HTTP GELF message to ", e));
} catch (ClientProtocolException e) {
errorReporter.reportError(e.getMessage(), new IOException("Cannot send data to " + uri + ":" + port, e));
} catch (IOException e) {
errorReporter.reportError(e.getMessage(), new IOException("Cannot send data to " + uri + ":" + port, e));
} finally {
if (httpResponse != null)
Closer.close(httpResponse);
}
return false;
}

@Override
public void close() {
Closer.close(httpClient);
}
}
7 changes: 7 additions & 0 deletions src/test/java/biz/paluch/logging/gelf/GelfUtilTest.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package biz.paluch.logging.gelf;

import static org.junit.Assert.assertEquals;

import biz.paluch.logging.gelf.intern.GelfMessage;
import biz.paluch.logging.gelf.jboss7.JBoss7JulLogEvent;
import org.jboss.logmanager.ExtLogRecord;
Expand Down Expand Up @@ -41,4 +42,10 @@ public void testProfilingLong() throws Exception {
assertEquals("12sec", message.getAdditonalFields().get(GelfUtil.MDC_REQUEST_DURATION));

}

@Test
public void addDefaultPortIfMissing() {
String url = GelfUtil.addDefaultPortIfMissing("http://example.com/foo", String.valueOf(1234));
assertEquals("http://example.com:1234/foo", url);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package biz.paluch.logging.gelf.intern.sender;

import biz.paluch.logging.gelf.intern.ErrorReporter;
import biz.paluch.logging.gelf.intern.GelfMessage;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicStatusLine;
import org.apache.http.util.EntityUtils;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.io.IOException;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.*;

/**
* (c) Alekandar - https://github.com/salex89
*/

public class GelfHTTPSenderTest {


@Mock
HttpClientBuilder builder;
@Mock
CloseableHttpClient closeableHttpClient;
@Mock
CloseableHttpResponse closeableHttpResponse;
@Mock
ErrorReporter errorReporter;

@Before
public void prepareMocks() throws IOException {
MockitoAnnotations.initMocks(this);
// when(closeableHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 202, "Accepted"));
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An integration test with the sender would be great. Netty is already on the class-path, you can build your own HTTP server, see https://github.com/netty/netty/tree/4.0/example/src/main/java/io/netty/example/http/helloworld

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, using https://github.com/square/okhttp/tree/master/mockwebserver#readme might be useful for those kind of tests without writing everything from scratch. IMHO OkHttp is also quite a good HTTP client library.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thx. I'm a bit concerned about pulling in dependencies. With one "lousy" logger appender you're forced to a dependency that might kill your app :(

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be a good case for optional dependencies (https://maven.apache.org/guides/introduction/introduction-to-optional-and-excludes-dependencies.html) or using the HTTP client included in the JDK (https://docs.oracle.com/javase/8/docs/api/java/net/HttpURLConnection.html).
The mockwebserver dependency would only be needed in the test scope of this project.

Ignore my remark about OkHttp, that's just an opinion. 😉

// when(builder.build()).thenReturn(closeableHttpClient);
// when(closeableHttpClient.execute((HttpUriRequest) any())).thenReturn(closeableHttpResponse);
}

@Test
public void sendMessageTest() throws IOException {
when(closeableHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 202, "Accepted"));
when(builder.build()).thenReturn(closeableHttpClient);
when(closeableHttpClient.execute((HttpUriRequest) any())).thenReturn(closeableHttpResponse);

String uri = "http://192.168.0.100/gelf";
GelfHTTPSender sender = new GelfHTTPSender(uri, 12201, errorReporter, builder);
GelfMessage gelfMessage = new GelfMessage();
boolean success = sender.sendMessage(gelfMessage);
verify(builder, times(1)).build();
assertTrue(success);
verifyZeroInteractions(errorReporter);
ArgumentCaptor<HttpPost> postCaptor = ArgumentCaptor.forClass(HttpPost.class);
verify(closeableHttpClient).execute(postCaptor.capture());
HttpPost executedPost = postCaptor.getValue();
assertEquals("http://192.168.0.100:12201/gelf", executedPost.getURI().toString());
assertEquals(gelfMessage.toJson(), EntityUtils.toString(executedPost.getEntity()));
}

@Test
public void sendMessageTestIncorrectUrl() throws IOException {
String uri = "adda";
GelfHTTPSender sender = new GelfHTTPSender(uri, 12201, errorReporter, HttpClientBuilder.create());
GelfMessage gelfMessage = new GelfMessage();
boolean success = sender.sendMessage(gelfMessage);
assertFalse(success);
// verifyZeroInteractions(errorReporter);
// ArgumentCaptor<HttpPost> postCaptor = ArgumentCaptor.forClass(HttpPost.class);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code that is commented out is code to be removed.

// verify(closeableHttpClient).execute(postCaptor.capture());
// HttpPost executedPost = postCaptor.getValue();
// assertEquals("http://192.168.0.100:12201/gelf", executedPost.getURI().toString());
// assertEquals(gelfMessage.toJson(), EntityUtils.toString(executedPost.getEntity()));
}
}