diff --git a/src/main/java/de/siegmar/logbackgelf/CompressionMethod.java b/src/main/java/de/siegmar/logbackgelf/CompressionMethod.java new file mode 100644 index 0000000..ada3296 --- /dev/null +++ b/src/main/java/de/siegmar/logbackgelf/CompressionMethod.java @@ -0,0 +1,39 @@ +/* + * Logback GELF - zero dependencies Logback GELF appender library. + * Copyright (C) 2019 Oliver Siegmar + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package de.siegmar.logbackgelf; + +import de.siegmar.logbackgelf.compressor.Compressor; +import de.siegmar.logbackgelf.compressor.GZIPCompressor; +import de.siegmar.logbackgelf.compressor.ZLIBCompressor; + +public enum CompressionMethod { + ZLIB(new ZLIBCompressor()), + GZIP(new GZIPCompressor()); + + private Compressor compressor; + + CompressionMethod(final Compressor compressor) { + this.compressor = compressor; + } + + public Compressor getCompressor() { + return this.compressor; + } +} diff --git a/src/main/java/de/siegmar/logbackgelf/GelfUdpAppender.java b/src/main/java/de/siegmar/logbackgelf/GelfUdpAppender.java index a25cb06..96d46bb 100644 --- a/src/main/java/de/siegmar/logbackgelf/GelfUdpAppender.java +++ b/src/main/java/de/siegmar/logbackgelf/GelfUdpAppender.java @@ -19,15 +19,15 @@ package de.siegmar.logbackgelf; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.channels.DatagramChannel; import java.util.function.Supplier; -import java.util.zip.DeflaterOutputStream; + +import de.siegmar.logbackgelf.compressor.Compressor; +import de.siegmar.logbackgelf.compressor.NoneCompressor; public class GelfUdpAppender extends AbstractGelfAppender { @@ -42,6 +42,11 @@ public class GelfUdpAppender extends AbstractGelfAppender { */ private boolean useCompression = true; + /** + * Compression method used if useCompression is true. Default: ZLIB. + */ + private CompressionMethod compressionMethod = CompressionMethod.ZLIB; + private Supplier messageIdSupplier = new MessageIdSupplier(); private RobustChannel robustChannel; @@ -50,6 +55,8 @@ public class GelfUdpAppender extends AbstractGelfAppender { private AddressResolver addressResolver; + private Compressor compressor; + public Integer getMaxChunkSize() { return maxChunkSize; } @@ -66,6 +73,14 @@ public void setUseCompression(final boolean useCompression) { this.useCompression = useCompression; } + public CompressionMethod getCompressionMethod() { + return compressionMethod; + } + + public void setCompressionMethod(final CompressionMethod compressionMethod) { + this.compressionMethod = compressionMethod; + } + public Supplier getMessageIdSupplier() { return messageIdSupplier; } @@ -79,14 +94,15 @@ protected void startAppender() throws IOException { robustChannel = new RobustChannel(); chunker = new GelfUdpChunker(messageIdSupplier, maxChunkSize); addressResolver = new AddressResolver(getGraylogHost()); + compressor = useCompression ? compressionMethod.getCompressor() : new NoneCompressor(); } @Override protected void appendMessage(final byte[] binMessage) throws IOException { - final byte[] messageToSend = useCompression ? compress(binMessage) : binMessage; + final byte[] messageToSend = compressor.compress(binMessage); final InetSocketAddress remote = new InetSocketAddress(addressResolver.resolve(), - getGraylogPort()); + getGraylogPort()); for (final ByteBuffer chunk : chunker.chunks(messageToSend)) { while (chunk.hasRemaining()) { @@ -95,16 +111,6 @@ protected void appendMessage(final byte[] binMessage) throws IOException { } } - private static byte[] compress(final byte[] binMessage) { - final ByteArrayOutputStream bos = new ByteArrayOutputStream(binMessage.length); - try (OutputStream deflaterOut = new DeflaterOutputStream(bos)) { - deflaterOut.write(binMessage); - } catch (IOException e) { - throw new IllegalStateException(e); - } - return bos.toByteArray(); - } - @Override protected void close() throws IOException { robustChannel.close(); diff --git a/src/main/java/de/siegmar/logbackgelf/compressor/Compressor.java b/src/main/java/de/siegmar/logbackgelf/compressor/Compressor.java new file mode 100644 index 0000000..8f4449b --- /dev/null +++ b/src/main/java/de/siegmar/logbackgelf/compressor/Compressor.java @@ -0,0 +1,27 @@ +/* + * Logback GELF - zero dependencies Logback GELF appender library. + * Copyright (C) 2016 Oliver Siegmar + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package de.siegmar.logbackgelf.compressor; + + +public interface Compressor { + + byte[] compress(byte[] binMessage); + +} diff --git a/src/main/java/de/siegmar/logbackgelf/compressor/GZIPCompressor.java b/src/main/java/de/siegmar/logbackgelf/compressor/GZIPCompressor.java new file mode 100644 index 0000000..daf50ad --- /dev/null +++ b/src/main/java/de/siegmar/logbackgelf/compressor/GZIPCompressor.java @@ -0,0 +1,40 @@ +/* + * Logback GELF - zero dependencies Logback GELF appender library. + * Copyright (C) 2019 Oliver Siegmar + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package de.siegmar.logbackgelf.compressor; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.zip.GZIPOutputStream; + +public class GZIPCompressor implements Compressor { + + @Override + public byte[] compress(final byte[] binMessage) { + final ByteArrayOutputStream bos = new ByteArrayOutputStream(binMessage.length); + try (OutputStream gzipOut = new GZIPOutputStream(bos)) { + gzipOut.write(binMessage); + } catch (IOException e) { + throw new IllegalStateException(e); + } + return bos.toByteArray(); + } + +} diff --git a/src/main/java/de/siegmar/logbackgelf/compressor/NoneCompressor.java b/src/main/java/de/siegmar/logbackgelf/compressor/NoneCompressor.java new file mode 100644 index 0000000..4032679 --- /dev/null +++ b/src/main/java/de/siegmar/logbackgelf/compressor/NoneCompressor.java @@ -0,0 +1,29 @@ +/* + * Logback GELF - zero dependencies Logback GELF appender library. + * Copyright (C) 2019 Oliver Siegmar + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package de.siegmar.logbackgelf.compressor; + +public class NoneCompressor implements Compressor { + + @Override + public byte[] compress(final byte[] binMessage) { + return binMessage; + } + +} diff --git a/src/main/java/de/siegmar/logbackgelf/compressor/ZLIBCompressor.java b/src/main/java/de/siegmar/logbackgelf/compressor/ZLIBCompressor.java new file mode 100644 index 0000000..428b0fd --- /dev/null +++ b/src/main/java/de/siegmar/logbackgelf/compressor/ZLIBCompressor.java @@ -0,0 +1,40 @@ +/* + * Logback GELF - zero dependencies Logback GELF appender library. + * Copyright (C) 2019 Oliver Siegmar + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package de.siegmar.logbackgelf.compressor; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.zip.DeflaterOutputStream; + +public class ZLIBCompressor implements Compressor { + + @Override + public byte[] compress(final byte[] binMessage) { + final ByteArrayOutputStream bos = new ByteArrayOutputStream(binMessage.length); + try (OutputStream deflaterOut = new DeflaterOutputStream(bos)) { + deflaterOut.write(binMessage); + } catch (IOException e) { + throw new IllegalStateException(e); + } + return bos.toByteArray(); + } + +} diff --git a/src/test/java/de/siegmar/logbackgelf/GelfUdpAppenderTest.java b/src/test/java/de/siegmar/logbackgelf/GelfUdpAppenderTest.java index 54cc681..da327fe 100644 --- a/src/test/java/de/siegmar/logbackgelf/GelfUdpAppenderTest.java +++ b/src/test/java/de/siegmar/logbackgelf/GelfUdpAppenderTest.java @@ -23,8 +23,11 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.io.UncheckedIOException; import java.net.DatagramPacket; import java.net.DatagramSocket; @@ -35,6 +38,7 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.zip.GZIPInputStream; import java.util.zip.InflaterOutputStream; import org.junit.jupiter.api.BeforeEach; @@ -80,14 +84,32 @@ public void simple() { } @Test - public void compression() { + public void compressionZLIB() { final Logger logger = setupLogger(true); logger.error("Test message"); stopLogger(logger); - final JsonNode jsonNode = receiveCompressedMessage(); + final JsonNode jsonNode = receiveCompressedMessage(CompressionMethod.ZLIB); + assertEquals("1.1", jsonNode.get("version").textValue()); + assertEquals("localhost", jsonNode.get("host").textValue()); + assertEquals("Test message", jsonNode.get("short_message").textValue()); + assertTrue(jsonNode.get("timestamp").isNumber()); + assertEquals(3, jsonNode.get("level").intValue()); + assertNotNull(jsonNode.get("_thread_name").textValue()); + assertEquals(LOGGER_NAME, jsonNode.get("_logger_name").textValue()); + } + + @Test + public void compressionGZIP() { + final Logger logger = setupLogger(true, CompressionMethod.GZIP); + + logger.error("Test message"); + + stopLogger(logger); + + final JsonNode jsonNode = receiveCompressedMessage(CompressionMethod.GZIP); assertEquals("1.1", jsonNode.get("version").textValue()); assertEquals("localhost", jsonNode.get("host").textValue()); assertEquals("Test message", jsonNode.get("short_message").textValue()); @@ -98,6 +120,10 @@ public void compression() { } private Logger setupLogger(final boolean useCompression) { + return setupLogger(useCompression, CompressionMethod.ZLIB); + } + + private Logger setupLogger(final boolean useCompression, final CompressionMethod compressionMethod) { final LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); final GelfEncoder gelfEncoder = new GelfEncoder(); @@ -106,13 +132,15 @@ private Logger setupLogger(final boolean useCompression) { gelfEncoder.start(); final Logger logger = (Logger) LoggerFactory.getLogger(LOGGER_NAME); - logger.addAppender(buildAppender(useCompression, lc, gelfEncoder)); + logger.addAppender(buildAppender(useCompression, compressionMethod, lc, gelfEncoder)); logger.setAdditive(false); return logger; } - private GelfUdpAppender buildAppender(final boolean useCompression, final LoggerContext lc, + private GelfUdpAppender buildAppender(final boolean useCompression, + final CompressionMethod compressionMethod, + final LoggerContext lc, final GelfEncoder gelfEncoder) { final GelfUdpAppender gelfAppender = new GelfUdpAppender(); gelfAppender.setContext(lc); @@ -121,6 +149,7 @@ private GelfUdpAppender buildAppender(final boolean useCompression, final Logger gelfAppender.setGraylogHost("localhost"); gelfAppender.setGraylogPort(port); gelfAppender.setUseCompression(useCompression); + gelfAppender.setCompressionMethod(compressionMethod); gelfAppender.start(); return gelfAppender; } @@ -133,15 +162,22 @@ private JsonNode receiveMessage() { } } - private JsonNode receiveCompressedMessage() { - final ByteArrayOutputStream bos = new ByteArrayOutputStream(); - final InflaterOutputStream inflaterOutputStream = new InflaterOutputStream(bos); + private JsonNode receiveCompressedMessage(final CompressionMethod compressionMethod) { try { - inflaterOutputStream.write(receive()); - inflaterOutputStream.close(); + if (compressionMethod == CompressionMethod.ZLIB) { + return new ObjectMapper().readTree(Decompressor.zlibDecompress(receive())); + } else { + return new ObjectMapper().readTree(Decompressor.gzipDecompress(receive())); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } - return new ObjectMapper().readTree(bos.toByteArray()); + private JsonNode receiveGZIPCompressedMessage() { + try { + return new ObjectMapper().readTree(new GZIPInputStream(new ByteArrayInputStream(receive()))); } catch (IOException e) { throw new UncheckedIOException(e); } @@ -186,4 +222,30 @@ public byte[] call() throws Exception { } } + private static final class Decompressor { + + public static byte[] zlibDecompress(final byte[] bytesIn) { + final ByteArrayOutputStream bos = new ByteArrayOutputStream(); + final OutputStream inflaterOutputStream = new InflaterOutputStream(bos); + + try { + inflaterOutputStream.write(bytesIn); + inflaterOutputStream.close(); + + return bos.toByteArray(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + public static InputStream gzipDecompress(final byte[] bytesIn) { + try { + return new GZIPInputStream(new ByteArrayInputStream(bytesIn)); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + } + }