Skip to content

Commit 2fcd902

Browse files
huksleyclaude
andauthored
Fix memory leak by limiting internal message queue size (#63)
* Fix memory leak by limiting internal message queue size This commit addresses GitHub issue #61 where memory usage keeps increasing and garbage collection fails, eventually causing node crashes after several days. Root Cause: The syslog4j library queues messages internally with no limit by default when using TCP/SSL protocols. Under high message throughput, this causes unbounded memory growth leading to OOM errors. Solution: 1. Added maxQueueSize configuration parameter (default: 500 messages) 2. Set maxQueueSize on all syslog config objects (UDP/TCP/SSL) 3. Made the parameter user-configurable through Graylog UI The conservative default of 500 prevents memory leaks while still allowing reasonable buffering. Users experiencing high throughput can tune this value based on their needs, but should avoid unlimited queuing (-1). When the queue is full, the plugin will block until space is available, providing natural backpressure instead of consuming all available memory. Configuration: - Default: 500 messages - Configurable via "Maximum queue size" field in output configuration - Set to -1 for unlimited (not recommended, will cause OOM) Fixes #61 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> * Clarify maxQueueSize field description Updated the description for maxQueueSize field to clarify its purpose. * Add maxQueueSize configuration for syslog output * Fix typo in variable name for UDP config --------- Co-authored-by: Claude <[email protected]>
1 parent 7401ea5 commit 2fcd902

File tree

1 file changed

+16
-2
lines changed

1 file changed

+16
-2
lines changed

src/main/java/com/wizecore/graylog2/plugin/SyslogOutput.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.graylog2.syslog4j.SyslogConfigIF;
1919
import org.graylog2.syslog4j.SyslogIF;
2020
import org.graylog2.syslog4j.SyslogRuntimeException;
21+
import org.graylog2.syslog4j.impl.AbstractSyslogConfigIF;
2122
import org.graylog2.syslog4j.impl.message.processor.SyslogMessageProcessor;
2223
import org.graylog2.syslog4j.impl.message.processor.structured.StructuredSyslogMessageProcessor;
2324
import org.graylog2.syslog4j.impl.net.tcp.TCPNetSyslogConfig;
@@ -92,13 +93,23 @@ public SyslogOutput(@Assisted Stream stream, @Assisted Configuration conf) {
9293
format = "plain";
9394
}
9495

96+
int maxQueueSize = 500;
97+
String queueSizeStr = conf.getString("maxQueueSize");
98+
if (queueSizeStr != null && !queueSizeStr.trim().isEmpty()) {
99+
maxQueueSize = Integer.parseInt(queueSizeStr);
100+
}
101+
95102
log.info("Creating syslog output " + protocol + "://" + host + ":" + port + ", format " + format);
96103
SyslogConfigIF config = null;
97104
if (protocol.toLowerCase().equals("udp")) {
98-
config = new UDPNetSyslogConfig();
105+
UDPNetSyslogConfig udpConfig = new UDPNetSyslogConfig();
106+
udpConfig.setMaxQueueSize(maxQueueSize);
107+
config = udpConfig;
99108
} else
100109
if (protocol.toLowerCase().equals("tcp")) {
101-
config = new TCPNetSyslogConfig();
110+
TCPNetSyslogConfig tcpConfig = new TCPNetSyslogConfig();
111+
tcpConfig.setMaxQueueSize(maxQueueSize);
112+
config = tcpConfig;
102113
} else
103114
if (protocol.toLowerCase().equals("tcp-ssl")) {
104115
CustomSSLSyslogConfig sslConfig = new CustomSSLSyslogConfig();
@@ -128,6 +139,7 @@ public SyslogOutput(@Assisted Stream stream, @Assisted Configuration conf) {
128139
sslConfig.setKeyStorePassword(ksp);
129140
sslConfig.setTrustStore(ts);
130141
sslConfig.setTrustStorePassword(tsp);
142+
sslConfig.setMaxQueueSize(maxQueueSize);
131143
} else {
132144
throw new IllegalArgumentException("Unknown protocol: " + protocol);
133145
}
@@ -320,6 +332,8 @@ public ConfigurationRequest getRequestedConfiguration() {
320332

321333
configurationRequest.addField(new TextField("maxlen", "Maximum message length", "", "Maximum message (body) length. Longer messages will be truncated. If not specified defaults to 16384 bytes.", ConfigurationField.Optional.OPTIONAL));
322334

335+
configurationRequest.addField(new TextField("maxQueueSize", "Maximum queue size", "500", "Maximum number of messages to queue internally. Set to -1 for unlimited. Default is 500.", ConfigurationField.Optional.OPTIONAL));
336+
323337
configurationRequest.addField(new TextField("keystore", "Key store", "", "Path to Java keystore (required for SSL over TCP). Must contain private key and cert for this client.", ConfigurationField.Optional.OPTIONAL));
324338
configurationRequest.addField(new TextField("keystorePassword", "Key store password", "", "", ConfigurationField.Optional.OPTIONAL));
325339

0 commit comments

Comments
 (0)