());
+ size = 0;
+ }
+
+ public void send(FormattedLogMessage log) throws LogzioServerErrorException {
+ if (size + log.getSize() > MAX_SIZE_IN_BYTES) {
+ sendAndReset();
+ }
+ messages.add(log);
+ size += log.getSize();
+ }
+
+ private void reset(){
+ size = 0;
+ messages.clear();
+ }
+
+ void flush() throws LogzioServerErrorException {
+ if(messages.size() > 0) {
+ sendAndReset();
+ }
+ }
+
+ private void sendAndReset() throws LogzioServerErrorException {
+ logzioClient.sendToLogzio(messages);
+ reset();
+ }
+
+ private static class Reporter implements SenderStatusReporter{
+ private static final Logger LOGGER = Logger.getLogger(LogzioDao.class.getName());
+
+ private void pringLogMessage(Level level, String msg) {
+ LOGGER.log(level, msg);
+ }
+
+ @Override
+ public void error(String msg) {
+ pringLogMessage(Level.SEVERE, "[LogzioSender]ERROR: " + msg);
+ }
+
+ @Override
+ public void error(String msg, Throwable e) {
+ pringLogMessage(Level.SEVERE, "[LogzioSender]ERROR: " + msg + "\n" +e);
+ }
+
+ @Override
+ public void warning(String msg) {
+ pringLogMessage(Level.WARNING, "[LogzioSender]WARNING: " + msg);
+ }
+
+ @Override
+ public void warning(String msg, Throwable e) {
+ pringLogMessage(Level.WARNING, "[LogzioSender]WARNING: " + msg + "\n" + e);
+ }
+
+ @Override
+ public void info(String msg) {
+ pringLogMessage(Level.INFO, "[LogzioSender]INFO: " + msg);
+ }
+
+ @Override
+ public void info(String msg, Throwable e) {
+ pringLogMessage(Level.INFO, "[LogzioSender]INFO: " + msg + "\n" + e);
+ }
+ }
+}
diff --git a/src/main/resources/index.jelly b/src/main/resources/index.jelly
index 8f22feef..147c6c6c 100644
--- a/src/main/resources/index.jelly
+++ b/src/main/resources/index.jelly
@@ -1,4 +1,4 @@
- Adds the possibility to push builds logs and build data to a Logstash indexer such as Redis, RabbitMQ, Elastic Search or to Syslog.
+ Adds the possibility to push builds logs and build data to a Logstash indexer such as Redis, RabbitMQ, Elastic Search, Logz.io or to Syslog.
diff --git a/src/main/resources/jenkins/plugins/logstash/configuration/Logzio/config.jelly b/src/main/resources/jenkins/plugins/logstash/configuration/Logzio/config.jelly
new file mode 100644
index 00000000..2f26e1f1
--- /dev/null
+++ b/src/main/resources/jenkins/plugins/logstash/configuration/Logzio/config.jelly
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/jenkins/plugins/logstash/configuration/Logzio/help-host.jelly b/src/main/resources/jenkins/plugins/logstash/configuration/Logzio/help-host.jelly
new file mode 100644
index 00000000..fe3675f4
--- /dev/null
+++ b/src/main/resources/jenkins/plugins/logstash/configuration/Logzio/help-host.jelly
@@ -0,0 +1,6 @@
+
+
+
Logz.io listener URL.
+ If you are in the EU region insert https://listener-eu.logz.io:8071. Otherwise, use https://listener.logz.io:8071
+ You can tell which region you are in by checking your login URL
+
\ No newline at end of file
diff --git a/src/main/resources/jenkins/plugins/logstash/configuration/Logzio/help-token.jelly b/src/main/resources/jenkins/plugins/logstash/configuration/Logzio/help-token.jelly
new file mode 100644
index 00000000..cfb2dc00
--- /dev/null
+++ b/src/main/resources/jenkins/plugins/logstash/configuration/Logzio/help-token.jelly
@@ -0,0 +1,4 @@
+
+
+
The Logz.io account token.
+
diff --git a/src/main/resources/jenkins/plugins/logstash/configuration/Logzio/help.jelly b/src/main/resources/jenkins/plugins/logstash/configuration/Logzio/help.jelly
new file mode 100644
index 00000000..d8e01e22
--- /dev/null
+++ b/src/main/resources/jenkins/plugins/logstash/configuration/Logzio/help.jelly
@@ -0,0 +1,4 @@
+
+
+ Push to Logz.io with HTTPs input.
+
\ No newline at end of file
diff --git a/src/test/java/jenkins/plugins/logstash/LogstashConfigurationMigrationTest.java b/src/test/java/jenkins/plugins/logstash/LogstashConfigurationMigrationTest.java
index 0785ffe7..61b5568d 100644
--- a/src/test/java/jenkins/plugins/logstash/LogstashConfigurationMigrationTest.java
+++ b/src/test/java/jenkins/plugins/logstash/LogstashConfigurationMigrationTest.java
@@ -150,5 +150,4 @@ public void rabbitMqMigration()
assertThat(es.getPassword(), equalTo("pwd"));
assertThat(es.getUsername(), equalTo("user"));
}
-
}
diff --git a/src/test/java/jenkins/plugins/logstash/LogstashOutputStreamTest.java b/src/test/java/jenkins/plugins/logstash/LogstashOutputStreamTest.java
index 7752bf38..e7b6d994 100644
--- a/src/test/java/jenkins/plugins/logstash/LogstashOutputStreamTest.java
+++ b/src/test/java/jenkins/plugins/logstash/LogstashOutputStreamTest.java
@@ -1,6 +1,7 @@
package jenkins.plugins.logstash;
import static org.hamcrest.core.StringContains.containsString;
+import static org.hamcrest.object.HasToString.hasToString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.*;
@@ -13,6 +14,7 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
@@ -122,4 +124,17 @@ public void eolSuccessNoDao() throws Exception {
assertEquals("Results don't match", msg, buffer.toString());
verify(mockWriter).isConnectionBroken();
}
+
+ @Test
+ public void writerClosedBeforeDelegate() throws Exception {
+ ByteArrayOutputStream mockBuffer = Mockito.spy(buffer);
+ new LogstashOutputStream(mockBuffer, mockWriter).close();
+
+ InOrder inOrder = Mockito.inOrder(mockBuffer, mockWriter);
+ inOrder.verify(mockWriter).close();
+ inOrder.verify(mockBuffer).close();
+
+ // Verify results
+ assertThat(buffer, hasToString(""));
+ }
}
diff --git a/src/test/java/jenkins/plugins/logstash/configuration/LogzioTest.java b/src/test/java/jenkins/plugins/logstash/configuration/LogzioTest.java
new file mode 100644
index 00000000..0f30fd05
--- /dev/null
+++ b/src/test/java/jenkins/plugins/logstash/configuration/LogzioTest.java
@@ -0,0 +1,44 @@
+package jenkins.plugins.logstash.configuration;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.jvnet.hudson.test.JenkinsRule;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+public class LogzioTest{
+ @Rule
+ public JenkinsRule j = new JenkinsRule();
+
+ private Logzio indexer;
+ private Logzio indexer2;
+
+ @Before
+ public void setup(){
+ indexer = new Logzio();
+ indexer.setHost("https://listener.logz.io:8071");
+ indexer.setToken("token");
+
+ indexer2 = new Logzio();
+ indexer2.setHost("https://listener.logz.io:8071");
+ indexer2.setToken("token");
+ }
+
+ @Test
+ public void sameSettingsAreEqual(){ assertThat(indexer.equals(indexer2), is(true)); }
+
+ @Test
+ public void tokenChangeIsNotEqual() {
+ indexer.setToken("newPassword");
+ assertThat(indexer.equals(indexer2), is(false));
+ }
+
+ @Test
+ public void hostChangeIsNotEqual() {
+ indexer.setHost("https://logz.io");
+ assertThat(indexer.equals(indexer2), is(false));
+ }
+
+}
diff --git a/src/test/java/jenkins/plugins/logstash/persistence/LogzioDaoTest.java b/src/test/java/jenkins/plugins/logstash/persistence/LogzioDaoTest.java
new file mode 100644
index 00000000..31b3c794
--- /dev/null
+++ b/src/test/java/jenkins/plugins/logstash/persistence/LogzioDaoTest.java
@@ -0,0 +1,162 @@
+package jenkins.plugins.logstash.persistence;
+
+import io.logz.sender.FormattedLogMessage;
+import io.logz.sender.exceptions.LogzioServerErrorException;
+import net.sf.json.JSONObject;
+import net.sf.json.test.JSONAssert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.powermock.api.mockito.PowerMockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class LogzioDaoTest {
+
+ private static final String data = "{\"a\":{\"b\":1,\"c\":2,\"d\":[false, true]},\"e\":\"f\",\"g\":2.3}";
+ private static final String flat_data = "\"a_b\":1,\"a_c\":2,\"a_d[0]\":false,\"a_d[1]\":true,\"e\":\"f\",\"g\":2.3";
+ private static final String EMPTY_STRING_WITH_DATA = "{\"@buildTimestamp\":\"2000-01-01\"," + flat_data + ",\"message\":[],\"source\":\"jenkins\",\"source_host\":\"http://localhost:8080/jenkins\",\"@version\":1}";
+ private static final String ONE_LINE_STRING_WITH_DATA = "{\"@buildTimestamp\":\"2000-01-01\"," + flat_data + ",\"message\":[\"LINE 1\"],\"source\":\"jenkins\",\"source_host\":\"http://localhost:8080/jenkins\",\"@version\":1}";
+ private static final String TWO_LINE_STRING_WITH_DATA = "{\"@buildTimestamp\":\"2000-01-01\"," + flat_data + ",\"message\":[\"LINE 1\", \"LINE 2\"],\"source\":\"jenkins\",\"source_host\":\"http://localhost:8080/jenkins\",\"@version\":1}";
+ private static final String EMPTY_STRING_NO_DATA = "{\"@buildTimestamp\":\"2000-01-01\",\"message\":[],\"source\":\"jenkins\",\"source_host\":\"http://localhost:8080/jenkins\",\"@version\":1}";
+ private static final String ONE_LINE_STRING_NO_DATA = "{\"@buildTimestamp\":\"2000-01-01\",\"message\":[\"LINE 1\"],\"source\":\"jenkins\",\"source_host\":\"http://localhost:8080/jenkins\",\"@version\":1}";
+ private static final String TWO_LINE_STRING_NO_DATA = "{\"@buildTimestamp\":\"2000-01-01\",\"message\":[\"LINE 1\", \"LINE 2\"],\"source\":\"jenkins\",\"source_host\":\"http://localhost:8080/jenkins\",\"@version\":1}";
+ private LogzioDao dao;
+
+ @Captor private ArgumentCaptor sendArgument = ArgumentCaptor.forClass(FormattedLogMessage.class);
+
+ @Mock private LogzioHttpsClient logzioSender;
+ @Mock private BuildData mockBuildData;
+
+ private LogzioDao createDao(String host, String key) throws IllegalArgumentException {
+ return new LogzioDao(logzioSender, host, key);
+ }
+
+ @Before
+ public void before() throws IllegalArgumentException, LogzioServerErrorException {
+ when(mockBuildData.getTimestamp()).thenReturn("2000-01-01");
+
+ doNothing().when(logzioSender).send(any(FormattedLogMessage.class));
+ doNothing().when(logzioSender).flush();
+
+ dao = createDao("http://localhost:8200/", "123456789");
+
+ }
+
+ @Test
+ public void constructorSuccess() throws IllegalArgumentException {
+ // Unit under test
+ dao = createDao("https://localhost:8201/", "123");
+
+ // Verify results
+ assertEquals("Wrong host name", "https://localhost:8201/", dao.getHost());
+ assertEquals("Wrong key", "123", dao.getKey());
+ }
+
+ @Test
+ public void buildPayloadSuccessEmpty(){
+ when(mockBuildData.toString()).thenReturn("{}");
+ // Unit under test
+ JSONObject result = dao.buildPayload(mockBuildData, "http://localhost:8080/jenkins", new ArrayList());
+ result.remove("@timestamp");
+
+ // Verify results
+ JSONAssert.assertEquals("Results don't match", JSONObject.fromObject(EMPTY_STRING_NO_DATA), result);
+ }
+
+ @Test
+ public void buildPayloadSuccessOneLine(){
+ when(mockBuildData.toString()).thenReturn("{}");
+ // Unit under test
+ JSONObject result = dao.buildPayload(mockBuildData, "http://localhost:8080/jenkins", Collections.singletonList("LINE 1"));
+ result.remove("@timestamp");
+
+ // Verify results
+ JSONAssert.assertEquals("Results don't match", JSONObject.fromObject(ONE_LINE_STRING_NO_DATA), result);
+ }
+
+ @Test
+ public void buildPayloadSuccessTwoLines(){
+ when(mockBuildData.toString()).thenReturn("{}");
+ // Unit under test
+ JSONObject result = dao.buildPayload(mockBuildData, "http://localhost:8080/jenkins", Arrays.asList("LINE 1", "LINE 2"));
+ result.remove("@timestamp");
+
+ // Verify results
+ JSONAssert.assertEquals("Results don't match", JSONObject.fromObject(TWO_LINE_STRING_NO_DATA), result);
+ }
+
+ @Test
+ public void buildPayloadWithDataSuccessEmpty(){
+ when(mockBuildData.toString()).thenReturn(data);
+ // Unit under test
+ JSONObject result = dao.buildPayload(mockBuildData, "http://localhost:8080/jenkins", new ArrayList());
+ result.remove("@timestamp");
+
+ // Verify results
+ JSONAssert.assertEquals("Results don't match", JSONObject.fromObject(EMPTY_STRING_WITH_DATA), result);
+ }
+
+ @Test
+ public void buildPayloadWithDataSuccessOneLine(){
+ when(mockBuildData.toString()).thenReturn(data);
+ // Unit under test
+ JSONObject result = dao.buildPayload(mockBuildData, "http://localhost:8080/jenkins", Collections.singletonList("LINE 1"));
+ result.remove("@timestamp");
+
+ // Verify results
+ JSONAssert.assertEquals("Results don't match", JSONObject.fromObject(ONE_LINE_STRING_WITH_DATA), result);
+ }
+
+ @Test
+ public void buildPayloadWithDataSuccessTwoLines(){
+ when(mockBuildData.toString()).thenReturn(data);
+ // Unit under test
+ JSONObject result = dao.buildPayload(mockBuildData, "http://localhost:8080/jenkins", Arrays.asList("LINE 1", "LINE 2"));
+ result.remove("@timestamp");
+
+ // Verify results
+ JSONAssert.assertEquals("Results don't match", JSONObject.fromObject(TWO_LINE_STRING_WITH_DATA), result);
+ }
+
+ @Test
+ public void pushNoMessage() throws IOException, LogzioServerErrorException {
+ // Unit under test
+ dao.push(EMPTY_STRING_WITH_DATA);
+ verify(logzioSender, never()).send(sendArgument.capture());
+ verify(logzioSender, never()).flush();
+ }
+
+ @Test
+ public void pushOneMessage() throws IOException, LogzioServerErrorException {
+ // Unit under test
+ dao.push(ONE_LINE_STRING_WITH_DATA);
+ // Verify results
+ verify(logzioSender, times(1)).send(sendArgument.capture());
+ verify(logzioSender, times(1)).flush();
+ }
+
+ @Test
+ public void pushMultiMessages() throws IOException, LogzioServerErrorException {
+ // Unit under test
+ dao.push(TWO_LINE_STRING_WITH_DATA);
+ // Verify results
+ verify(logzioSender, times(2)).send(sendArgument.capture());
+ verify(logzioSender, times(1)).flush();
+ }
+}
diff --git a/src/test/resources/logzio.xml b/src/test/resources/logzio.xml
new file mode 100644
index 00000000..5e81bdd1
--- /dev/null
+++ b/src/test/resources/logzio.xml
@@ -0,0 +1,8 @@
+
+
+
+ https://listener.logz.io:8071
+ {AQAAABAAAAAQAwaAxyveddM0PF+kR0dYFAymdth9PpitQnvJW0SR6JU=}
+
+ true
+
\ No newline at end of file