diff --git a/README.adoc b/README.adoc index 1f7f24a..e9c9622 100644 --- a/README.adoc +++ b/README.adoc @@ -1,48 +1,88 @@ image::https://scan.coverity.com/projects/22709/badge.svg[link=https://scan.coverity.com/projects/jla_01] -= Relp Logging plugin for Logback +== RELP Logging plugin for Logback See link:https://github.com/teragrep/jla_01/blob/master/src/main/resources/logback.example.xml[logback.example.xml] for example config == Parameters |=== -|Parameter |Description +|Parameter |Description | Default |`relpHostAddress` |Connection destination address +|127.0.0.1 |`relpPort` |Connection destination port +|601 |`enableEventId48577` |Enables structured data containing uuid and source information +|true |`appName` |Stream application identifier. Maximum length of 48 characters, limited by RFC5424 +|jla-01 |`hostname` |Stream host identifier. Maximum length of 255 characters, limited by RFC5424 +|localhost.localdomain |`connectionTimeout` -|Time to wait before timing out connection +|Time to wait before timing out connection in milliseconds +|2500 |`reconnectInterval` -|Time to wait between re-connection attempts +|Time to wait between re-connection attempts in milliseconds +|500 |`writeTimeout` -|Time to wait for destination to accept data +|Time to wait for destination to accept data in milliseconds +|1500 |`readTimeout` -|Time to wait for destination to acknowledge sent data (low values cause duplicates) +|Time to wait for destination to acknowledge sent data (low values cause duplicates) in milliseconds +|1500 |`keepAlive` -|Enables sending alive packets. Default true. +|Enables sending alive packets. +|true |`reconnectIfNoMessagesInterval` -|Reconnects before sending message if at least X milliseconds have passed since last message. Default 150000, set to 0 to turn off automatic reconnections. +|Reconnects before sending message if at least X milliseconds have passed since last message. Set to 0 to turn off automatic reconnections. +|150000 + +|`connectOnStart` +|Start one connection initially at appender start. Allows detection of configuration mistakes early. +|false + +|`rebindEnabled` +|Rebind RELP connection after `rebindAmount` of records. +|true + +|`rebindAmount` +|Rebind after this amount of records sent if `rebindEnabled` is set. +|100000 + +|`synchronizedAccess` +|Allows only one thread at a time to append, therefore uses only one connection. +|false + +|`useTLS` +|Use TLS instead of a plain text connection. +|false + +|`keystorePath` +|Path to Java keystore that includes the CA certificate for the TLS connection +|/unset/path/to/keystore + +|`keystorePassword` +|Keystore password for the keystore defined in `keystorePath` +| |=== + == jboss-module These instructions are untested but should work none the less. diff --git a/src/main/java/com/teragrep/jla_01/IRelpAppenderConfig.java b/src/main/java/com/teragrep/jla_01/IRelpAppenderConfig.java index c592f30..48f9d82 100644 --- a/src/main/java/com/teragrep/jla_01/IRelpAppenderConfig.java +++ b/src/main/java/com/teragrep/jla_01/IRelpAppenderConfig.java @@ -26,8 +26,6 @@ Reliable Event Logging Protocol (RELP) Logback plugin public interface IRelpAppenderConfig { void setEncoder(LayoutWrappingEncoder encoder); - void setSender(RelpConnection sender); - void setRelpPort(int relpPort); void setEnableEventId48577(Boolean enableEventId48577); @@ -60,4 +58,12 @@ public interface IRelpAppenderConfig { void setKeystorePassword(String keystorePassword); void setTlsProtocol(String tlsProtocol); + + void setConnectOnStart(boolean connectOnStart); + + void setRebindEnabled(boolean rebindEnabled); + + void setRebindAmount(int rebindAmount); + + void setSynchronizedAccess(boolean synchronizedAccess); } diff --git a/src/main/java/com/teragrep/jla_01/LoggingEventConverter.java b/src/main/java/com/teragrep/jla_01/LoggingEventConverter.java deleted file mode 100644 index aae0aa0..0000000 --- a/src/main/java/com/teragrep/jla_01/LoggingEventConverter.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - Reliable Event Logging Protocol (RELP) Logback plugin - Copyright (C) 2021 Suomen Kanuuna Oy - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -package com.teragrep.jla_01; - -import java.util.Date; -import java.util.Map; - -import com.teragrep.rlo_14.Facility; -import com.teragrep.rlo_14.SyslogMessage; -import com.teragrep.rlo_14.SDElement; -import com.teragrep.rlo_14.Severity; - -public class LoggingEventConverter { - - private LoggingEventConverter() { - } - - public static SyslogMessage getDefaultSyslogMessageWithSDElement(String message, String appName, String hostname, - SDElement metadata48577) { - if (metadata48577 == null) { - return getDefaultSyslogMessage(message, appName, hostname); - } - - SyslogMessage syslog = new SyslogMessage().withTimestamp(new Date().getTime()).withSeverity(Severity.WARNING) - .withAppName(appName) - .withHostname(hostname) - .withFacility(Facility.USER) - .withSDElement(metadata48577) - .withMsg(message); - return syslog; - } - - public static SyslogMessage getDefaultSyslogMessage(String message, String appName, String hostname) { - SyslogMessage syslog = new SyslogMessage().withTimestamp(new Date().getTime()).withSeverity(Severity.WARNING) - .withAppName(appName) - .withHostname(hostname) - .withFacility(Facility.USER).withMsg(message); - return syslog; - } - - public static SyslogMessage addSDElement(SyslogMessage syslog, String sdElementName, - Map parameters) { - if (syslog == null) { - return syslog; - } - - SDElement metadataSDE = getStructuredDataParams(sdElementName, parameters); - - - return syslog.withSDElement(metadataSDE); - } - - /* - * Gets the data from the mdc and adds it to SD Element as SD Parameters. - */ - private static SDElement getStructuredDataParams(String name, Map parameters) { - SDElement metadataSDE = new SDElement(name == null ? "" : name); - if (parameters == null) { - return metadataSDE; - } - parameters.forEach((k, v) -> { - metadataSDE.addSDParam(k, v); - }); - return metadataSDE; - } - -} diff --git a/src/main/java/com/teragrep/jla_01/RelpAppender.java b/src/main/java/com/teragrep/jla_01/RelpAppender.java new file mode 100644 index 0000000..f60d709 --- /dev/null +++ b/src/main/java/com/teragrep/jla_01/RelpAppender.java @@ -0,0 +1,23 @@ +/* + Reliable Event Logging Protocol (RELP) Logback plugin + Copyright (C) 2021-2024 Suomen Kanuuna Oy + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +package com.teragrep.jla_01; + +import com.teragrep.rlp_01.pool.Stubable; + +public interface RelpAppender extends Stubable { + + void append(E iLoggingEvent); + void stop(); +} diff --git a/src/main/java/com/teragrep/jla_01/RelpAppenderImpl.java b/src/main/java/com/teragrep/jla_01/RelpAppenderImpl.java new file mode 100644 index 0000000..eea9963 --- /dev/null +++ b/src/main/java/com/teragrep/jla_01/RelpAppenderImpl.java @@ -0,0 +1,73 @@ +/* + Reliable Event Logging Protocol (RELP) Logback plugin + Copyright (C) 2021-2024 Suomen Kanuuna Oy + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +package com.teragrep.jla_01; + +import ch.qos.logback.core.encoder.LayoutWrappingEncoder; +import com.teragrep.jla_01.syslog.*; +import com.teragrep.rlp_01.client.IManagedRelpConnection; +import com.teragrep.rlp_01.pool.Pool; + +import java.nio.charset.StandardCharsets; + +public final class RelpAppenderImpl implements RelpAppender { + + private final Pool relpConnectionPool; + private final String hostname; + private final String appName; + private final String originalHostname; + private final boolean enableEventId48577; + private final LayoutWrappingEncoder encoder; + + public RelpAppenderImpl(Pool relpConnectionPool, String hostname, String appName, String originalHostname, boolean enableEventId48577, LayoutWrappingEncoder encoder) { + this.relpConnectionPool = relpConnectionPool; + this.hostname = hostname; + this.appName = appName; + this.originalHostname = originalHostname; + this.enableEventId48577 = enableEventId48577; + this.encoder = encoder; + } + + @Override + public void append(E iLoggingEvent) { + { + SyslogRecord syslogRecord = new SyslogRecordConfigured(hostname, appName); + syslogRecord = new SyslogRecordTimestamp(syslogRecord); + syslogRecord = new SyslogRecordOrigin(syslogRecord, originalHostname); + if (enableEventId48577) { + syslogRecord = new SyslogRecordEventID(syslogRecord, originalHostname); + } + + //syslogRecord = new SyslogRecordMDC(syslogRecord, new HashMap<>()); + + String payload = encoder.getLayout().doLayout(iLoggingEvent); + syslogRecord = new SyslogRecordPayload(syslogRecord, payload); + + IManagedRelpConnection connection = relpConnectionPool.get(); + + connection.ensureSent(syslogRecord.getRecord().toRfc5424SyslogMessage().getBytes(StandardCharsets.UTF_8)); + relpConnectionPool.offer(connection); + } + } + + @Override + public void stop() { + relpConnectionPool.close(); + } + + @Override + public boolean isStub() { + return false; + } +} diff --git a/src/main/java/com/teragrep/jla_01/RelpAppenderStub.java b/src/main/java/com/teragrep/jla_01/RelpAppenderStub.java new file mode 100644 index 0000000..bb50b70 --- /dev/null +++ b/src/main/java/com/teragrep/jla_01/RelpAppenderStub.java @@ -0,0 +1,32 @@ +/* + Reliable Event Logging Protocol (RELP) Logback plugin + Copyright (C) 2021-2024 Suomen Kanuuna Oy + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +package com.teragrep.jla_01; + +public final class RelpAppenderStub implements RelpAppender { + @Override + public void append(E iLoggingEvent) { + throw new UnsupportedOperationException("RelpAppenderStub does not support this. Perhaps appender is not started yet."); + } + + @Override + public void stop() { + throw new UnsupportedOperationException("RelpAppenderStub does not support this. Perhaps appender is not started yet."); + } + + @Override + public boolean isStub() { + return true; + } +} diff --git a/src/main/java/com/teragrep/jla_01/RelpAppenderSynchronized.java b/src/main/java/com/teragrep/jla_01/RelpAppenderSynchronized.java new file mode 100644 index 0000000..f88c2cb --- /dev/null +++ b/src/main/java/com/teragrep/jla_01/RelpAppenderSynchronized.java @@ -0,0 +1,39 @@ +/* + Reliable Event Logging Protocol (RELP) Logback plugin + Copyright (C) 2021-2024 Suomen Kanuuna Oy + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +package com.teragrep.jla_01; + +public final class RelpAppenderSynchronized implements RelpAppender { + + private final RelpAppender appender; + + public RelpAppenderSynchronized(RelpAppender relpAppender) { + this.appender = relpAppender; + } + + @Override + public synchronized void append(E iLoggingEvent) { + appender.append(iLoggingEvent); + } + + @Override + public void stop() { + appender.stop(); + } + + @Override + public boolean isStub() { + return false; + } +} diff --git a/src/main/java/com/teragrep/jla_01/RlpLogbackAppender.java b/src/main/java/com/teragrep/jla_01/RlpLogbackAppender.java index 4f4aeca..43e4658 100644 --- a/src/main/java/com/teragrep/jla_01/RlpLogbackAppender.java +++ b/src/main/java/com/teragrep/jla_01/RlpLogbackAppender.java @@ -1,361 +1,367 @@ /* Reliable Event Logging Protocol (RELP) Logback plugin Copyright (C) 2021-2024 Suomen Kanuuna Oy - + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ - package com.teragrep.jla_01; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.security.GeneralSecurityException; -import java.util.UUID; -import java.util.concurrent.TimeoutException; -import java.util.function.Supplier; - +import ch.qos.logback.core.UnsynchronizedAppenderBase; import ch.qos.logback.core.encoder.LayoutWrappingEncoder; -import com.teragrep.rlo_14.SDElement; -import com.teragrep.rlo_14.SyslogMessage; - -import com.teragrep.rlp_01.RelpBatch; -import ch.qos.logback.core.AppenderBase; -import com.teragrep.rlp_01.RelpConnection; -import com.teragrep.rlp_01.SSLContextFactory; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLEngine; - -public class RlpLogbackAppender extends AppenderBase implements IRelpAppenderConfig { - - // see also https://stackoverflow.com/questions/31415899/correct-way-to-stop-custom-logback-async-appender - - private RelpConnection sender; - private LayoutWrappingEncoder encoder; - - // settings for syslog messages - private boolean enableEventId48577; - private String hostname = ""; - private String appName = ""; - private String realHostname = ""; - - // settings for relp window - private String relpHostAddress = "localhost"; - private int relpPort = 1234; - - // - //settings for timeouts, if they are 0 that we skip them - //default are 0 - private int connectionTimeout = 0; - private int readTimeout = 0; - private int writeTimeout = 0; - private int reconnectInterval = 500; - private boolean keepAlive = true; - private long reconnectIfNoMessagesInterval = 150000; - private long lastMessageSent = 0; - - // tls - private boolean useTLS = false; - private String keystorePath = ""; - private String keystorePassword = ""; - private String tlsProtocol = ""; - - - @Override - public void setEncoder(LayoutWrappingEncoder encoder) { - this.encoder = encoder; - } - - @Override - public void setSender(RelpConnection sender) { - this.sender = sender; - } - - @Override - public void setRelpPort(int relpPort) { - this.relpPort = relpPort; - } - - @Override - public void setEnableEventId48577(Boolean enableEventId48577) { - this.enableEventId48577 = enableEventId48577; - } - - @Override - public void setRelpHostAddress(String relpHostAddress) { - this.relpHostAddress = relpHostAddress; - } - - @Override - public void setAppName(String appName) { - this.appName = appName; - } - - @Override - public void setHostname(String hostname) { - this.hostname = hostname; - } - - // set connectionTimeout in seconds - @Override - public void setConnectionTimeout(int timeout) { - if (timeout > 0) { - this.connectionTimeout = timeout; - } - } - - @Override - public void setWriteTimeout(int timeout) { - if (timeout > 0) { - this.writeTimeout = timeout; - } - } - - @Override - public void setReadTimeout(int timeout) { - if (timeout > 0) { - this.readTimeout = timeout; - } - } - - //set reconnectInterval in milliseconds - @Override - public void setReconnectInterval(int interval) { - if (interval > 0) { - this.reconnectInterval = interval; - } - } - - @Override - public void setKeepAlive(boolean on) { - this.keepAlive=on; - } - - @Override - public void setReconnectIfNoMessagesInterval(int interval) { - this.reconnectIfNoMessagesInterval = interval; - } - - // tls - @Override - public void setUseTLS(boolean on) { - this.useTLS = on; - } - - @Override - public void setKeystorePath(String keystorePath) { - this.keystorePath = keystorePath; - } - - @Override - public void setKeystorePassword(String keystorePassword) { - this.keystorePassword = keystorePassword; - } - - @Override - public void setTlsProtocol(String tlsProtocol) { - this.tlsProtocol = tlsProtocol; - } - - private void connect() { - if (System.getenv("JLA01_DEBUG") != null) { - System.out.println("RlpLogbackAppender.connect>"); - } - - boolean notConnected = true; - while (notConnected) { - boolean connected = false; - try { - realHostname = java.net.InetAddress.getLocalHost().getHostName(); - connected = this.sender.connect(this.relpHostAddress, this.relpPort); - } catch (Exception e) { - System.out.println("RlpLogbackAppender.connect> reconnecting to relp server <["+relpHostAddress+"]> at port <["+relpPort+"]> after <[" + reconnectInterval + "]> milliseconds due to exception <" + e.getMessage() +">"); - } - if (connected) { - notConnected = false; - } else { - try { - Thread.sleep(this.reconnectInterval); - } catch (InterruptedException e) { - System.out.println("RlpLogbackAppender.connect> reconnect timer interrupted, reconnecting now"); - } - } - } - } - - private void tearDown() { - if (System.getenv("JLA01_DEBUG") != null) { - System.out.println("RlpLogbackAppender.tearDown>"); - } - this.sender.tearDown(); - } - - private void disconnect() { - if (System.getenv("JLA01_DEBUG") != null) { - System.out.println("RlpLogbackAppender.disconnect>"); - } - boolean disconnected = false; - try { - disconnected = this.sender.disconnect(); - } catch (IllegalStateException | IOException | TimeoutException e) { - System.out.println("RlpLogbackAppender.disconnect> forcefully closing connection due to exception <" + e.getMessage() + ">"); - } - finally { - this.tearDown(); - } - if (System.getenv("JLA01_DEBUG") != null) { - System.out.println("RlpLogbackAppender.disconnect> disconnected: " + disconnected); - } - } - - @Override - public void start() { - if (started) - return; - - // initialize events sender - if (useTLS) { - Supplier sslEngineSupplier = new Supplier() { - private final SSLContext sslContext; - { - try { - sslContext = SSLContextFactory.authenticatedContext(keystorePath, keystorePassword, tlsProtocol); - } catch (GeneralSecurityException | IOException e) { - throw new RuntimeException(e); - } - } - - @Override - public SSLEngine get() { - return sslContext.createSSLEngine(); - } - }; - - this.sender = new RelpConnection(sslEngineSupplier); - } - else { - this.sender = new RelpConnection(); - } - - - this.sender.setConnectionTimeout(connectionTimeout); - this.sender.setReadTimeout(this.readTimeout); - this.sender.setWriteTimeout(this.writeTimeout); - this.sender.setKeepAlive(this.keepAlive); - - this.connect(); - super.start(); - } - - - - @Override - public void stop() { - if (System.getenv("JLA01_DEBUG") != null) { - System.out.println("RlpLogbackAppender.stop>"); - } - if (!started) - return; - - this.disconnect(); - super.stop(); - } - - @Override - protected void append(E eventObject) { - if (System.getenv("JLA01_DEBUG") != null) { - System.out.println("RlpLogbackAppender.append> entry"); - } - String logMessage = encoder.getLayout().doLayout(eventObject); - - if (logMessage == null) { - throw new IllegalArgumentException("layout not able to encode event to string"); - } - - final RelpBatch relpBatch = new RelpBatch(); - - if (enableEventId48577) { - final SDElement event_id_48577 = eventId48577(); - final SDElement origin_48577 = origin48577(); - SyslogMessage sl = LoggingEventConverter.getDefaultSyslogMessageWithSDElement(logMessage, appName, hostname, - event_id_48577); - sl.withSDElement(origin_48577); - relpBatch.insert(sl.toRfc5424SyslogMessage().getBytes(StandardCharsets.UTF_8)); - } else { - SyslogMessage sl = LoggingEventConverter.getDefaultSyslogMessage(logMessage, appName, hostname); - relpBatch.insert(sl.toRfc5424SyslogMessage().getBytes(StandardCharsets.UTF_8)); - } - - boolean notSent = true; - if ( - reconnectIfNoMessagesInterval > 0 && - System.currentTimeMillis() > (lastMessageSent + reconnectIfNoMessagesInterval) - ) { - this.tearDown(); - this.connect(); - } - while (notSent) { - try { - this.sender.commit(relpBatch); - } catch (IllegalStateException | IOException | java.util.concurrent.TimeoutException e) { - System.out.println("RlpLogbackAppender.append> will retry sending due to exception <" + e.getMessage() + ">"); - } - - if (!relpBatch.verifyTransactionAll()) { - relpBatch.retryAllFailed(); - this.tearDown(); - this.connect(); - } else { - notSent = false; - } - } - lastMessageSent = System.currentTimeMillis(); - if (System.getenv("JLA01_DEBUG") != null) { - System.out.println("RlpLogbackAppender.append> exit"); - } - } - - - private SDElement eventId48577() { - final SDElement event_id_48577 = new SDElement("event_id@48577"); - if (realHostname != null) { - event_id_48577.addSDParam("hostname", realHostname); - } - else { - event_id_48577.addSDParam("hostname", ""); - } - - String uuid = UUID.randomUUID().toString(); - event_id_48577.addSDParam("uuid", uuid); - event_id_48577.addSDParam("source", "source"); - - long unixtime = System.currentTimeMillis(); - String epochtime = Long.toString(unixtime); - event_id_48577.addSDParam("unixtime", epochtime); - return event_id_48577; - - } - - - private SDElement origin48577() { - final SDElement origin_48577 = new SDElement("origin@48577"); - if (realHostname != null) { - origin_48577.addSDParam("hostname", realHostname); - } - else { - origin_48577.addSDParam("hostname", ""); - } - - return origin_48577; - } +import com.teragrep.jla_01.syslog.hostname.Hostname; +import com.teragrep.rlp_01.client.*; +import com.teragrep.rlp_01.pool.Pool; +import com.teragrep.rlp_01.pool.UnboundPool; + +import java.time.Duration; +import java.util.concurrent.atomic.AtomicReference; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + * Unsynchronized version of RELP Appender for Logback. It is configured in a Bean manner using setters by Logback. + * @param + */ +public final class RlpLogbackAppender extends UnsynchronizedAppenderBase implements IRelpAppenderConfig { + + private LayoutWrappingEncoder encoder; + + private int relpPort; + private Boolean enableEventId48577; + private String relpHostAddress; + private String appName; + private String hostname; + private int connectionTimeout; + private int writeTimeout; + private int readTimeout; + private int reconnectInterval; + private boolean keepAliveEnabled; + private int reconnectIfNoMessagesInterval; + private boolean connectOnStart; + private boolean rebindEnabled; + private int rebindAmount; + private boolean synchronizedAccess; + + + private boolean useTls; + private String keystorePath; + private String keystorePassword; + private String tlsProtocol; + + private final Lock beanLock = new ReentrantLock(); + + private final AtomicReference> relpAppenderRef; + + public RlpLogbackAppender() { + // just defaults here + + encoder = new LayoutWrappingEncoder<>(); + + relpPort = 601; + enableEventId48577 = true; + relpHostAddress = "127.0.0.1"; + appName = "jla-01"; + hostname = "localhost.localdomain"; + connectionTimeout = 2500; + writeTimeout = 1500; + readTimeout = 1500; + reconnectInterval = 500; + keepAliveEnabled = true; + reconnectIfNoMessagesInterval = 150000; + connectOnStart = false; + rebindEnabled = true; + rebindAmount = 100000; + synchronizedAccess = false; + + useTls = false; + keystorePath = "/unset/path/to/keystore"; + keystorePassword = ""; + tlsProtocol = "TLSv1.3"; + + relpAppenderRef = new AtomicReference<>(new RelpAppenderStub<>()); + } + + @Override + public void setEncoder(LayoutWrappingEncoder encoder) { + beanLock.lock(); + try { + this.encoder = encoder; + } + finally { + beanLock.unlock(); + } + } + + @Override + public void setRelpPort(int relpPort) { + beanLock.lock(); + try { + this.relpPort = relpPort; + } + finally { + beanLock.lock(); + } + } + + @Override + public void setEnableEventId48577(Boolean enableEventId48577) { + beanLock.lock(); + try { + this.enableEventId48577 = enableEventId48577; + } + finally { + beanLock.lock(); + } + } + + @Override + public void setRelpHostAddress(String relpHostAddress) { + beanLock.lock(); + try { + this.relpHostAddress = relpHostAddress; + } + finally { + beanLock.unlock(); + } + } + + @Override + public void setAppName(String appName) { + beanLock.lock(); + try { + this.appName = appName; + } + finally { + beanLock.unlock(); + } + } + + @Override + public void setHostname(String hostname) { + beanLock.lock(); + try { + this.hostname = hostname; + } + finally { + beanLock.lock(); + } + } + + @Override + public void setConnectionTimeout(int timeout) { + beanLock.lock(); + try { + this.connectionTimeout = timeout; + } + finally { + beanLock.lock(); + } + } + + @Override + public void setWriteTimeout(int timeout) { + beanLock.lock(); + try { + this.writeTimeout = timeout; + } + finally { + beanLock.lock(); + } + } + + @Override + public void setReadTimeout(int timeout) { + beanLock.lock(); + try { + this.readTimeout = timeout; + } + finally { + beanLock.unlock(); + } + } + + @Override + public void setReconnectInterval(int interval) { + beanLock.lock(); + try { + this.reconnectInterval = interval; + } + finally { + beanLock.unlock(); + } + } + + @Override + public void setKeepAlive(boolean on) { + beanLock.lock(); + try { + this.keepAliveEnabled = on; + } + finally { + beanLock.unlock(); + } + } + + @Override + public void setReconnectIfNoMessagesInterval(int interval) { + beanLock.lock(); + try { + this.reconnectIfNoMessagesInterval = interval; + } + finally { + beanLock.unlock(); + } + } + + @Override + public void setUseTLS(boolean on) { + beanLock.lock(); + try { + this.useTls = on; + } + finally { + beanLock.unlock(); + } + } + + @Override + public void setKeystorePath(String keystorePath) { + beanLock.lock(); + try { + this.keystorePath = keystorePath; + } + finally { + beanLock.unlock(); + } + } + + @Override + public void setKeystorePassword(String keystorePassword) { + beanLock.lock(); + try { + this.keystorePassword = keystorePassword; + } + finally { + beanLock.unlock(); + } + } + + @Override + public void setTlsProtocol(String tlsProtocol) { + beanLock.lock(); + try { + this.tlsProtocol = tlsProtocol; + } + finally { + beanLock.unlock(); + } + } + + @Override + public void setConnectOnStart(boolean connectOnStart) { + beanLock.lock(); + try { + this.connectOnStart = connectOnStart; + } + finally { + beanLock.unlock(); + } + } + + @Override + public void setRebindEnabled(boolean rebindEnabled) { + beanLock.lock(); + try { + this.rebindEnabled = rebindEnabled; + } + finally { + beanLock.unlock(); + } + } + + @Override + public void setRebindAmount(int rebindAmount) { + beanLock.lock(); + try { + this.rebindAmount = rebindAmount; + } + finally { + beanLock.unlock(); + } + } + + @Override + public void setSynchronizedAccess(boolean synchronizedAccess) { + beanLock.lock(); + try { + this.synchronizedAccess = synchronizedAccess; + } + finally { + beanLock.unlock(); + } + } + + @Override + public void start() { + beanLock.lock(); + try { + String originalHostname = new Hostname("").hostname(); + + boolean maxIdleEnabled = (reconnectIfNoMessagesInterval > 0); + + final RelpConfig relpConfig = new RelpConfig(relpHostAddress, relpPort, reconnectInterval, rebindAmount, rebindEnabled, Duration.ofMillis(reconnectIfNoMessagesInterval), maxIdleEnabled); + + final SocketConfig socketConfig = new SocketConfigImpl(readTimeout, writeTimeout, connectionTimeout, keepAliveEnabled); + + final SSLContextSupplier sslContextSupplier; + if (useTls) { + sslContextSupplier = new SSLContextSupplierKeystore(keystorePath, keystorePassword, tlsProtocol); + } else { + sslContextSupplier = new SSLContextSupplierStub(); + } + + RelpConnectionFactory relpConnectionFactory + = new RelpConnectionFactory(relpConfig, socketConfig, sslContextSupplier); + + Pool relpConnectionPool = new UnboundPool<>(relpConnectionFactory, new ManagedRelpConnectionStub()); + + if (connectOnStart) { + IManagedRelpConnection managedRelpConnection = relpConnectionPool.get(); + relpConnectionPool.offer(managedRelpConnection); + } + + RelpAppender relpAppender = new RelpAppenderImpl<>(relpConnectionPool,hostname, appName, originalHostname, enableEventId48577, encoder); + + if (synchronizedAccess) { + relpAppender = new RelpAppenderSynchronized<>(relpAppender); + } + + relpAppenderRef.set(relpAppender); + } + finally { + beanLock.unlock(); + } + super.start(); + } + + @Override + public void stop() { + super.stop(); + relpAppenderRef.get().stop(); + } + + @Override + protected void append(E iLoggingEvent) { + relpAppenderRef.get().append(iLoggingEvent); + } + + } diff --git a/src/main/java/com/teragrep/jla_01/syslog/SyslogRecord.java b/src/main/java/com/teragrep/jla_01/syslog/SyslogRecord.java new file mode 100644 index 0000000..4e8b5e3 --- /dev/null +++ b/src/main/java/com/teragrep/jla_01/syslog/SyslogRecord.java @@ -0,0 +1,21 @@ +/* + Reliable Event Logging Protocol (RELP) Logback plugin + Copyright (C) 2021-2024 Suomen Kanuuna Oy + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +package com.teragrep.jla_01.syslog; + +import com.teragrep.rlo_14.SyslogMessage; + +public interface SyslogRecord { + SyslogMessage getRecord(); +} diff --git a/src/main/java/com/teragrep/jla_01/syslog/SyslogRecordConfigured.java b/src/main/java/com/teragrep/jla_01/syslog/SyslogRecordConfigured.java new file mode 100644 index 0000000..59aebb9 --- /dev/null +++ b/src/main/java/com/teragrep/jla_01/syslog/SyslogRecordConfigured.java @@ -0,0 +1,49 @@ +/* + Reliable Event Logging Protocol (RELP) Logback plugin + Copyright (C) 2021-2024 Suomen Kanuuna Oy + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +package com.teragrep.jla_01.syslog; + +import com.teragrep.rlo_14.Facility; +import com.teragrep.rlo_14.Severity; +import com.teragrep.rlo_14.SyslogMessage; + +public final class SyslogRecordConfigured implements SyslogRecord { + + private final String hostname; + private final String appName; + private final Severity severity; + private final Facility facility; + + public SyslogRecordConfigured(String hostname, String appName) { + this(hostname, appName, Severity.WARNING, Facility.USER); + } + + public SyslogRecordConfigured(String hostname, String appName, Severity severity, Facility facility) { + this.hostname = hostname; + this.appName = appName; + this.severity = severity; + this.facility = facility; + } + + @Override + public SyslogMessage getRecord() { + SyslogMessage syslogMessage = new SyslogMessage(); + syslogMessage = syslogMessage.withFacility(facility); + syslogMessage = syslogMessage.withSeverity(severity); + syslogMessage = syslogMessage.withHostname(hostname); + syslogMessage = syslogMessage.withAppName(appName); + + return syslogMessage; + } +} diff --git a/src/main/java/com/teragrep/jla_01/syslog/SyslogRecordEventID.java b/src/main/java/com/teragrep/jla_01/syslog/SyslogRecordEventID.java new file mode 100644 index 0000000..76792f8 --- /dev/null +++ b/src/main/java/com/teragrep/jla_01/syslog/SyslogRecordEventID.java @@ -0,0 +1,48 @@ +/* + Reliable Event Logging Protocol (RELP) Logback plugin + Copyright (C) 2021-2024 Suomen Kanuuna Oy + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +package com.teragrep.jla_01.syslog; + +import com.teragrep.rlo_14.SDElement; +import com.teragrep.rlo_14.SyslogMessage; +import java.util.UUID; + +public final class SyslogRecordEventID implements SyslogRecord { + + private final SyslogRecord syslogRecord; + private final String hostname; + + + public SyslogRecordEventID(SyslogRecord syslogRecord, String hostname) { + this.syslogRecord = syslogRecord; + this.hostname = hostname; + } + + @Override + public SyslogMessage getRecord() { + final SDElement eventIdSDE = new SDElement("event_id@48577"); + + eventIdSDE.addSDParam("hostname", hostname); + + String uuid = UUID.randomUUID().toString(); + eventIdSDE.addSDParam("uuid", uuid); + eventIdSDE.addSDParam("source", "source"); + + long unixtime = System.currentTimeMillis(); + String epochtime = Long.toString(unixtime); + eventIdSDE.addSDParam("unixtime", epochtime); + + return syslogRecord.getRecord().withSDElement(eventIdSDE); + } +} diff --git a/src/main/java/com/teragrep/jla_01/syslog/SyslogRecordMDC.java b/src/main/java/com/teragrep/jla_01/syslog/SyslogRecordMDC.java new file mode 100644 index 0000000..7920752 --- /dev/null +++ b/src/main/java/com/teragrep/jla_01/syslog/SyslogRecordMDC.java @@ -0,0 +1,38 @@ +/* + Reliable Event Logging Protocol (RELP) Logback plugin + Copyright (C) 2021-2024 Suomen Kanuuna Oy + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +package com.teragrep.jla_01.syslog; + +import com.teragrep.rlo_14.SDElement; +import com.teragrep.rlo_14.SyslogMessage; + +import java.util.Map; + +public final class SyslogRecordMDC implements SyslogRecord { + + private final SyslogRecord syslogRecord; + private final Map mdc; + + public SyslogRecordMDC(SyslogRecord syslogRecord, Map mdc) { + this.syslogRecord = syslogRecord; + this.mdc = mdc; + } + + @Override + public SyslogMessage getRecord() { + final SDElement mdcElement = new SDElement("jla_01_mdc@48577"); + mdc.forEach(mdcElement::addSDParam); + return syslogRecord.getRecord().withSDElement(mdcElement); + } +} diff --git a/src/main/java/com/teragrep/jla_01/syslog/SyslogRecordOrigin.java b/src/main/java/com/teragrep/jla_01/syslog/SyslogRecordOrigin.java new file mode 100644 index 0000000..f47e4eb --- /dev/null +++ b/src/main/java/com/teragrep/jla_01/syslog/SyslogRecordOrigin.java @@ -0,0 +1,37 @@ +/* + Reliable Event Logging Protocol (RELP) Logback plugin + Copyright (C) 2021-2024 Suomen Kanuuna Oy + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +package com.teragrep.jla_01.syslog; + +import com.teragrep.rlo_14.SDElement; +import com.teragrep.rlo_14.SyslogMessage; + +public final class SyslogRecordOrigin implements SyslogRecord { + + private final SyslogRecord syslogRecord; + private final String hostname; + public SyslogRecordOrigin(SyslogRecord syslogRecord, String hostname) { + this.syslogRecord = syslogRecord; + this.hostname = hostname; + } + + @Override + public SyslogMessage getRecord() { + final SDElement origin = new SDElement("origin@48577"); + origin.addSDParam("hostname", hostname); + + return syslogRecord.getRecord().withSDElement(origin); + } + +} diff --git a/src/main/java/com/teragrep/jla_01/syslog/SyslogRecordPayload.java b/src/main/java/com/teragrep/jla_01/syslog/SyslogRecordPayload.java new file mode 100644 index 0000000..7e7926c --- /dev/null +++ b/src/main/java/com/teragrep/jla_01/syslog/SyslogRecordPayload.java @@ -0,0 +1,34 @@ +/* + Reliable Event Logging Protocol (RELP) Logback plugin + Copyright (C) 2021-2024 Suomen Kanuuna Oy + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +package com.teragrep.jla_01.syslog; + +import com.teragrep.rlo_14.SyslogMessage; + +public final class SyslogRecordPayload implements SyslogRecord { + + private final SyslogRecord syslogRecord; + private final String payload; + public SyslogRecordPayload(SyslogRecord syslogRecord, String payload) { + this.syslogRecord = syslogRecord; + this.payload = payload; + } + + @Override + public SyslogMessage getRecord() { + SyslogMessage syslogMessage = syslogRecord.getRecord(); + syslogMessage.withMsg(payload); + return syslogMessage; + } +} diff --git a/src/main/java/com/teragrep/jla_01/syslog/SyslogRecordTimestamp.java b/src/main/java/com/teragrep/jla_01/syslog/SyslogRecordTimestamp.java new file mode 100644 index 0000000..b3e00b3 --- /dev/null +++ b/src/main/java/com/teragrep/jla_01/syslog/SyslogRecordTimestamp.java @@ -0,0 +1,39 @@ +/* + Reliable Event Logging Protocol (RELP) Logback plugin + Copyright (C) 2021-2024 Suomen Kanuuna Oy + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ +package com.teragrep.jla_01.syslog; + +import com.teragrep.rlo_14.SyslogMessage; + +import java.time.Instant; + +public final class SyslogRecordTimestamp implements SyslogRecord { + + private final SyslogRecord syslogRecord; + private final Instant timestamp; + + public SyslogRecordTimestamp(SyslogRecord syslogRecord) { + this(syslogRecord, Instant.now()); + } + + public SyslogRecordTimestamp(SyslogRecord syslogRecord, Instant timestamp) { + this.syslogRecord = syslogRecord; + this.timestamp = timestamp; + } + + @Override + public SyslogMessage getRecord() { + return syslogRecord.getRecord().withTimestamp(timestamp); + } +} diff --git a/src/main/java/com/teragrep/jla_01/syslog/hostname/Hostname.java b/src/main/java/com/teragrep/jla_01/syslog/hostname/Hostname.java new file mode 100644 index 0000000..34ad3ee --- /dev/null +++ b/src/main/java/com/teragrep/jla_01/syslog/hostname/Hostname.java @@ -0,0 +1,24 @@ +package com.teragrep.jla_01.syslog.hostname; + +import java.net.InetAddress; +import java.net.UnknownHostException; + +public final class Hostname { + private final String defaultHostname; + + public Hostname(final String defaultHostname) { + this.defaultHostname = defaultHostname; + } + + public String hostname() { + String rv; + try { + rv = InetAddress.getLocalHost().getHostName(); + } + catch (UnknownHostException e) { + rv = defaultHostname; + System.err.println("Could not determine hostname, defaulting to <["+defaultHostname+"]>"); + } + return rv; + } +} diff --git a/src/test/java/com/teragrep/jla_01/LoggingEventConverterTest.java b/src/test/java/com/teragrep/jla_01/LoggingEventConverterTest.java deleted file mode 100644 index 52dc1df..0000000 --- a/src/test/java/com/teragrep/jla_01/LoggingEventConverterTest.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - Reliable Event Logging Protocol (RELP) Logback plugin - Copyright (C) 2021 Suomen Kanuuna Oy - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -package com.teragrep.jla_01; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.time.LocalDate; -import java.time.ZoneId; -import java.time.format.DateTimeFormatter; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import com.teragrep.rlo_14.SyslogMessage; -import ch.qos.logback.classic.spi.ILoggingEvent; - -public class LoggingEventConverterTest { - - @Test - public void testgetDefaultSyslogMessage() { - ILoggingEvent loggingEventTest = new TestILoggingEvent(); - Map mdc = new HashMap(); - loggingEventTest.getMDCPropertyMap().put("ack_id", "100"); - SyslogMessage sm = LoggingEventConverter.getDefaultSyslogMessage( - "log event", - "", - ""); - System.out.println(sm.toRfc5424SyslogMessage()); - Assertions.assertNotNull(sm); - - } - - - @Test - public void testEvent_id_48577() { - - ILoggingEvent loggingEventTest = new TestILoggingEvent(); - - //[ event_id@48577 - // hostname=${current.hostname} - // uuid=${generated.uuid} - // unixtime=${epochtime} - // id_source="source"] - // structured element with a boolean configurable - // that says enableEventId48577=true/false from logback.xml - - Map parameters = new LinkedHashMap(); - parameters.put("hostname", "hostname"); - parameters.put("uuid", "uuid"); - - LocalDate ld = LocalDate.parse("2020-01-02", DateTimeFormatter.ISO_DATE); - long unixtime = ld.atStartOfDay(ZoneId.systemDefault()).toEpochSecond() * 1000; - // long unixtime = Instant.now().toEpochMilli(); - parameters.put("unixtime", Long.toString(unixtime)); - parameters.put("source", "source"); - - SyslogMessage sm = LoggingEventConverter.getDefaultSyslogMessage( - "log event", - "", - ""); - LoggingEventConverter.addSDElement(sm, "event_id@48577", parameters); - - String expected = " - - [event_id@48577 hostname=\"hostname\" uuid=\"uuid\" unixtime=\""+ unixtime +"\" source=\"source\"] log event"; - assertEquals(expected, sm.toRfc5424SyslogMessage().substring(30)); - System.out.println(sm.toRfc5424SyslogMessage()); - } - - - @Test - public void testEvent48577() { - - ILoggingEvent loggingEventTest = new TestILoggingEvent(); - - //[ event_id@48577 - // hostname=${current.hostname} - // uuid=${generated.uuid} - // unixtime=${epochtime} - // id_source="source"] - // structured element with a boolean configurable - // that says enableEventId48577=true/false from logback.xml - - - - Map parameters = new LinkedHashMap(); - parameters.put("hostname", "hostname"); - parameters.put("uuid", "uuid"); - - LocalDate ld = LocalDate.parse("2020-01-02", DateTimeFormatter.ISO_DATE); - long unixtime = ld.atStartOfDay(ZoneId.systemDefault()).toEpochSecond() * 1000; - // long unixtime = Instant.now().toEpochMilli(); - parameters.put("unixtime", Long.toString(unixtime)); - parameters.put("source", "source"); - - SyslogMessage sm = LoggingEventConverter.getDefaultSyslogMessage( - "log event", - "", - ""); - LoggingEventConverter.addSDElement(sm, "event_id@48577", parameters); - - String expected = " - - [event_id@48577 hostname=\"hostname\" uuid=\"uuid\" unixtime=\""+ unixtime +"\" source=\"source\"] log event"; - assertEquals(expected, sm.toRfc5424SyslogMessage().substring(30)); - System.out.println(sm.toRfc5424SyslogMessage()); - } -} diff --git a/src/test/java/com/teragrep/jla_01/RlpLogbackAppenderTest.java b/src/test/java/com/teragrep/jla_01/RlpLogbackAppenderTest.java index 2b88c85..18dfe42 100644 --- a/src/test/java/com/teragrep/jla_01/RlpLogbackAppenderTest.java +++ b/src/test/java/com/teragrep/jla_01/RlpLogbackAppenderTest.java @@ -18,9 +18,15 @@ Reliable Event Logging Protocol (RELP) Logback plugin package com.teragrep.jla_01; import java.io.ByteArrayInputStream; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ForkJoinPool; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import ch.qos.logback.classic.LoggerContext; import com.teragrep.jla_01.server.TestServer; @@ -43,6 +49,8 @@ public void testDefaultSyslogMessage() { final int serverPort = 22601; + final String testPayload = "some payload"; + final ConcurrentLinkedDeque messageList = new ConcurrentLinkedDeque<>(); AtomicLong openCount = new AtomicLong(); AtomicLong closeCount = new AtomicLong(); @@ -51,7 +59,7 @@ public void testDefaultSyslogMessage() { try (TestServer server = serverFactory.create(serverPort, messageList, openCount, closeCount)) { server.run(); - ILoggingEvent eventObject = new TestILoggingEvent(); + ILoggingEvent eventObject = new TestILoggingEvent(testPayload); LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); @@ -89,11 +97,11 @@ public void testDefaultSyslogMessage() { Assertions.assertEquals("localhost", rfc5424Frame.hostname.toString()); Assertions.assertEquals("appName", rfc5424Frame.appName.toString()); - Assertions.assertEquals("DEBUG logger - none\n", rfc5424Frame.msg.toString()); + Assertions.assertEquals("DEBUG logger - "+testPayload+"\n", rfc5424Frame.msg.toString()); } - Assertions.assertTrue (openCount.get() >= 1, "openCount not expected"); + Assertions.assertEquals (1, openCount.get(), "openCount not expected"); Assertions.assertEquals(1, closeCount.get(), "closeCount not expected"); } @@ -103,6 +111,7 @@ public void testDefaultSyslogMessageWithSDElement() { TestServerFactory serverFactory = new TestServerFactory(); final int serverPort = 22602; + final String testPayload = "test string two"; final ConcurrentLinkedDeque messageList = new ConcurrentLinkedDeque<>(); AtomicLong openCount = new AtomicLong(); @@ -113,7 +122,7 @@ public void testDefaultSyslogMessageWithSDElement() { server.run(); - TestILoggingEvent eventObject = new TestILoggingEvent(); + TestILoggingEvent eventObject = new TestILoggingEvent(testPayload); LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); @@ -149,11 +158,106 @@ public void testDefaultSyslogMessageWithSDElement() { Assertions.assertEquals("host1", rfc5424Frame.hostname.toString()); Assertions.assertEquals("appName", rfc5424Frame.appName.toString()); - Assertions.assertEquals("DEBUG logger - none\n", rfc5424Frame.msg.toString()); + Assertions.assertEquals("DEBUG logger - "+testPayload+"\n", rfc5424Frame.msg.toString()); } - Assertions.assertTrue(openCount.get() >= 1, "openCount not expected"); + Assertions.assertEquals(1, openCount.get(), "openCount not expected"); Assertions.assertEquals(1, closeCount.get(), "closeCount not expected"); } + + @Test + public void threadedTest() { + TestServerFactory serverFactory = new TestServerFactory(); + + final int serverPort = 22603; + final int testCycles = 10_000; + final String appName = "someApp"; + final String hostname = "someHost"; + + final ConcurrentLinkedDeque messageList = new ConcurrentLinkedDeque<>(); + AtomicLong openCount = new AtomicLong(); + AtomicLong closeCount = new AtomicLong(); + + Assertions.assertDoesNotThrow(() -> { + try (TestServer server = serverFactory.create(serverPort, messageList, openCount, closeCount)) { + server.run(); + + + LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); + + + PatternLayoutEncoder encoder = new PatternLayoutEncoder(); + encoder.setPattern("%-5level %logger{36} - %msg%n"); + encoder.setContext(loggerContext); + encoder.start(); + + + RlpLogbackAppender relpAppender = new RlpLogbackAppender<>(); + relpAppender.setEncoder(encoder); + relpAppender.setAppName(appName); + relpAppender.setHostname(hostname); + relpAppender.setRelpPort(serverPort); + relpAppender.start(); + + CountDownLatch countDownLatch = new CountDownLatch(testCycles); + + for (int i = 0; i < testCycles; i++) { + final String testString = "hey this is relp " + i; + ForkJoinPool.commonPool().submit(() -> { + + ILoggingEvent eventObject = new TestILoggingEvent(testString); + relpAppender.append(eventObject); + + countDownLatch.countDown(); + }); + } + + countDownLatch.await(); + relpAppender.stop(); + + + } + }); + + + Assertions.assertEquals(testCycles, messageList.size(), "messageList size not expected"); + + Pattern pattern = Pattern.compile("DEBUG logger - hey this is relp (\\d+)\n"); + + Map testIterationsMap = new HashMap<>(); + for (int i = 0; i < testCycles; i++) { + testIterationsMap.put(i,true); + } + + for (byte[] message : messageList) { + RFC5424Frame rfc5424Frame = new RFC5424Frame(); + rfc5424Frame.load(new ByteArrayInputStream(message)); + + AtomicBoolean frameNext = new AtomicBoolean(); + Assertions.assertDoesNotThrow( () -> {frameNext.set(rfc5424Frame.next());}); + Assertions.assertTrue(frameNext.get()); + + Assertions.assertEquals(hostname, rfc5424Frame.hostname.toString()); + Assertions.assertEquals(appName, rfc5424Frame.appName.toString()); + + Matcher matcher = pattern.matcher(rfc5424Frame.msg.toString()); + boolean matches = matcher.matches(); + Assertions.assertTrue(matches, "payload unexpected"); + + String testIterationValue = matcher.group(1); + + Assertions.assertDoesNotThrow( () -> {Integer.parseInt(testIterationValue);}, "extracted test iteration not integer"); + + int testIteration = Integer.parseInt(testIterationValue); + + Boolean iterationValue = testIterationsMap.remove(testIteration); + Assertions.assertNotNull(iterationValue); + Assertions.assertTrue(iterationValue); + } + Assertions.assertTrue(testIterationsMap.isEmpty(), "testIterationsMap was not empty: some messages were not delivered successfully"); + + Assertions.assertTrue( openCount.get() >= 1, "openCount not expected"); + Assertions.assertTrue(closeCount.get() >= 1, "closeCount not expected"); + } } diff --git a/src/test/java/com/teragrep/jla_01/TestILoggingEvent.java b/src/test/java/com/teragrep/jla_01/TestILoggingEvent.java index dc1fb9e..d10700a 100644 --- a/src/test/java/com/teragrep/jla_01/TestILoggingEvent.java +++ b/src/test/java/com/teragrep/jla_01/TestILoggingEvent.java @@ -29,6 +29,12 @@ Reliable Event Logging Protocol (RELP) Logback plugin import ch.qos.logback.classic.spi.LoggerContextVO; public class TestILoggingEvent implements ILoggingEvent { + + private final String payload; + + public TestILoggingEvent(String payload) { + this.payload = payload; + } Map mdc = new HashMap(); Object[] getArgumentArray; @@ -45,7 +51,7 @@ public Level getLevel() { @Override public String getMessage() { - return StringUtils.EMPTY; + return payload; } @Override @@ -57,7 +63,7 @@ public Object[] getArgumentArray() { @Override public String getFormattedMessage() { // TODO Auto-generated method stub - return "none"; + return payload; } @Override