Skip to content

Ignore out-of-range batch, queue, retry, timeout and flush settings with a warning - #45

Merged
PetrHeinz merged 4 commits into
mainfrom
claude/validate-settings
Sep 29, 2026
Merged

PetrHeinz merged 4 commits into
mainfrom
claude/validate-settings

Conversation

@PetrHeinz

@PetrHeinz PetrHeinz commented Sep 29, 2026 •

Copy link
Copy Markdown
Member

Since #44, setBatchInterval() only stores the value and start() schedules the sender with it, so a batchInterval of 0 or less makes start() throw the IllegalArgumentException of scheduleWithFixedDelay(). Measured with <batchInterval>0</batchInterval> (and -1) in a logback.xml that also has a console and a file appender:

  • logback 1.2.13 reports RuntimeException in Action for tag [appender] java.lang.IllegalArgumentException and leaves the Logtail appender unstarted: nothing is sent, the console and the file keep logging.
  • logback 1.3.16 and 1.5.38 fail the whole configuration (Failed to initialize or to run Configurator), so the console and the file log nothing either. A Spring Boot 3.5.16 app with the setting in logback-spring.xml does not start at all ("Logging system failed to initialize").
  • 0.3.8 and main before Start the sender in start(), not in the constructor #44 threw from the setter instead, after cancelling the running schedule: logback warned Failed to set property [batchInterval] to value "0" and the appender kept running without its periodic sends, so only full batches and the flush at JVM exit went out.

A negative maxFlushTime (new in #35, not released yet) goes to Thread.join(), which throws timeout value is negative. The shutdown hook dies at once and the JVM exits without sending the queue (0 of 100 queued lines arrived, on JDK 8 and 21), and stop() throws, which also aborts logback's reset in LoggerContext.stop(). logback's own AsyncAppender, which maxFlushTime is modelled on, throws from stop() on a negative value too, but has no shutdown hook that would lose the queue.

The other numeric settings were never checked either, and some of their values keep the appender from sending anything while logback's status shows no error. This is as old as the settings, 0.3.8 behaves the same unless noted:

  • batchSize 0 makes every flush send an empty batch and start over, since it never takes a line off the queue: against a local endpoint, about 18,000 empty requests a second over one kept connection and none of the queued lines, until the JVM exits, which the shutdown hook holds for the full maxFlushTime while it waits for that flush. 0.3.8 looped the same way over a new connection per request, and its JVM did not exit at all. A negative batchSize fails every attempt on batch.subList(0, -1) instead and, once the retries are used up, logs Error trying to call Better Stack : fromIndex(0) > toIndex(-1) in a tight loop (about 600,000 times a second in a probe).
  • maxQueueSize 0 or less queues nothing, so nothing is sent. 0 logs Maximum number of messages in queue reached (0) once, a negative value nothing at all.
  • A negative maxRetries drops every batch before its first attempt (Dropped batch of 3 logs.).
  • A negative connectTimeout or readTimeout makes HttpURLConnection throw timeouts can't be negative on every request, so every batch fails, is retried 5 times and dropped.

Each of these setters now keeps the value it had instead:

  • setBatchInterval() ignores a value below 1 with a warning in logback's status (batchInterval must be positive, keeping 3000 ms instead of 0), keeps the current interval and leaves a running sender alone. Valid values behave as before.
  • setMaxFlushTime() ignores a negative value the same way (maxFlushTime must be 0 (no limit) or more, keeping 30000 ms instead of -1). 0 still means no limit.
  • setBatchSize() and setMaxQueueSize() ignore a value below 1, setMaxRetries(), setConnectTimeout() and setReadTimeout() a negative one, the same way (batchSize must be positive, keeping 1000 instead of 0, maxRetries must be 0 (no retries) or more, keeping 5 instead of -1, connectTimeout must be 0 (no timeout) or more, keeping 5000 ms instead of -1). 0 still means no retries and no timeout. A negative retrySleepMilliseconds is left alone: TimeUnit.sleep() returns at once, so it only means no pause between retries.
  • A warning rather than an error, because Spring Boot refuses to start on a logback error status; logback prints warnings when it configures itself.

Measured with the jars of main (64cef67) and of this branch (3548ce4 for the first two tables, a7a53c2 for the third) on JDK 21, against a local endpoint that counts the lines of each request. For batchInterval, the logback.xml has a console, the Logtail and a file appender, and the app logs 3 lines and waits 4.5 s:

logback batchInterval main this branch
1.2.13 0, -1 error status, Logtail appender not started, nothing sent; console and file log warning printed, appender started, 3 lines sent 3.2 s after launch; console and file log
1.3.16, 1.5.38 0, -1 configuration fails: nothing on the console, in the file or sent warning printed, appender started, 3 lines sent 3.2 s after launch; console and file log
1.2.13, 1.3.16, 1.5.38 500 3 lines sent 0.7 s after launch 3 lines sent 0.7 s after launch, no warning

For maxFlushTime -1, with batchInterval 60000, the app queues 100 lines and returns from main without stopping logback:

logback main this branch
1.2.13, 1.3.16, 1.5.38 the shutdown hook dies with timeout value is negative, 0 of 100 lines arrive warning printed, 100 of 100 lines arrive at exit

For the other settings, the same logback.xml has one of them changed and the app again logs 3 lines and waits 4.5 s. logback 1.2.13 and 1.5.38 behave the same, the console and the file log the 3 lines in every run, and no run records an error status:

Setting main this branch
batchSize 0 0 of 3 lines sent, about 18,000 empty requests a second, still running when killed 7 s after launch warning printed, 3 lines sent 3.2 s after launch, exits at 4.7 s
maxQueueSize 0 0 of 3 lines sent, Maximum number of messages in queue reached (0) warning printed, 3 lines sent 3.2 s after launch
maxRetries -1 0 of 3 lines sent, Dropped batch of 3 logs. warning printed, 3 lines sent 3.2 s after launch
connectTimeout -1, readTimeout -1 (a run each) 0 of 3 lines sent, 6 × timeouts can't be negative, then Dropped batch of 3 logs. warning printed, 3 lines sent 3.2 s after launch
batchSize 1, maxQueueSize 10, maxRetries 0, connectTimeout 0, readTimeout 0 3 lines sent 0.2 s after launch, a request each the same, no warning

The commits come in two red/green pairs. The first commit only adds the tests for batchInterval and maxFlushTime and fails on CI with four of them (run): LogtailAppenderLifecycleTest configures two appenders from XML with 0 and -1 (two error statuses from the IllegalArgumentException instead of started appenders and warnings), sets 0 in code before start() and -5 on a running appender (both throw IllegalArgumentException), and LogtailAppenderMaxFlushTimeTest stops an appender with maxFlushTime -1 (stop() throws timeout value is negative). The second commit makes them pass. The third commit adds the tests for the other settings and fails on CI with five of them on every JDK (run): LogtailAppenderOutOfRangeSettingsTest configures an appender per setting from XML (no warnings, the values taken as they are) and sets a batch size, a queue size and a number of retries in code (the invalid value replaces the one set before), and LogtailAppenderTimeoutTest does the same for both timeouts. Each of them checks the setting before anything is logged, so the red run never starts the endless flush. The fourth commit makes them pass.

🤖 Generated with Claude Code

PetrHeinz and others added 4 commits September 29, 2026 15:42
…ignored with a warning

A batchInterval below 1 ms must not keep the appender from starting: from a
logback configuration it must start without an error status, keep the
default 3000 ms and send on it; set in code before start() or on a running
appender it must keep the interval set before and send on that schedule. A
negative maxFlushTime must not make stop() throw: the queue is sent and the
default 30000 ms is kept. Each ignored value leaves a warning in logback's
status.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…warning

setBatchInterval() stored 0 or less for start() to schedule the sender
with, which throws IllegalArgumentException: logback 1.2 left the appender
unstarted, logback 1.3+ failed the whole configuration. On a running
appender it cancelled the sender before throwing. A negative maxFlushTime
made Thread.join() throw in stop() and in the shutdown hook, which then let
the JVM exit without sending the queue. Both setters now keep the current
value and add a warning to logback's status instead; 0 still means no limit
for maxFlushTime.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ngs are ignored with a warning

A batchSize or maxQueueSize below 1, a negative maxRetries and a negative
connectTimeout or readTimeout must be ignored: from a logback configuration
the appender keeps the default, records a warning and no error in logback's
status and still sends; set in code it keeps the value set before, which
also pins that 0 retries and a timeout of 0 (none) stay valid. A kept batch
size still splits the queue into batches, a kept queue size still limits
the queue. Each test checks the kept value before anything is logged, so on
the current code they fail at once, without starting a flush.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…arning

A batchSize of 0 made every flush send an empty batch and loop without end
(below 0 it failed on every attempt), so nothing queued was ever sent. A
maxQueueSize below 1 queued nothing, a negative maxRetries dropped every
batch before its first attempt, and a negative connectTimeout or
readTimeout made HttpURLConnection throw on every request, so every batch
was dropped after its retries. The setters now keep the current value and
add a warning to logback's status instead, like batchInterval and
maxFlushTime; 0 still means no retries for maxRetries and no timeout for
the timeouts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@PetrHeinz PetrHeinz changed the title Ignore a batchInterval below 1 ms and a negative maxFlushTime Ignore out-of-range batch, queue, retry, timeout and flush settings with a warning Sep 29, 2026
@PetrHeinz
PetrHeinz marked this pull request as ready for review September 29, 2026 16:55
@PetrHeinz
PetrHeinz merged commit c269114 into main Sep 29, 2026
6 checks passed
@PetrHeinz
PetrHeinz deleted the claude/validate-settings branch September 29, 2026 16:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant