ramanathan1504 opened a new issue, #4242:
URL: https://github.com/apache/logging-log4j2/issues/4242

   ## Description
   
   When `CassandraManager.startupInternal()` fails, the `Cluster` it created is 
never closed, and because the DataStax 3.x driver's threads are **non-daemon**, 
the JVM never exits. An application that logs to Cassandra and fails to start 
the appender hangs on shutdown forever, having reported nothing worse than a 
status-logger line.
   
   Two things combine:
   
   **1. The `Cluster` is built in the factory, but only closed from 
`shutdownInternal()`.**
   
   ```java
   @Override
   protected void startupInternal() throws Exception {
       session = cluster.connect(keyspace);          // throws -> session stays 
null
       preparedStatement = session.prepare(insertQueryTemplate);
   }
   
   @Override
   protected boolean shutdownInternal() throws Exception {
       session.close();                              // would NPE anyway if 
reached
       cluster.close();                              // the only close() for 
the Cluster
       return true;
   }
   ```
   
   **2. `AbstractDatabaseManager.shutdown()` is guarded by `if 
(this.isRunning())`**, and a failed startup leaves `running = false` — so 
`shutdownInternal()` is never called and `cluster.close()` never runs. 
`LogManager.shutdown()` reports success:
   
   ```
   main DEBUG Shutting down CassandraManager Cassandra
   main DEBUG Shut down CassandraManager Cassandra, all resources released: true
   ```
   
   while five non-daemon driver threads remain alive, each carrying the 
appender's configured `clusterName`:
   
   ```
   "log4j-bench-nio-worker-0"             non-daemon
   "log4j-bench-nio-worker-1"             non-daemon
   "log4j-bench-connection-reaper-0"      non-daemon
   "log4j-bench-scheduled-task-worker-0"  non-daemon
   "log4j-bench-timeouter-0"              non-daemon
   ```
   
   The general guard asymmetry is #4241, since it affects every 
`AbstractDatabaseManager`; this issue is the concrete case where the 
consequence is a hung JVM rather than noisy logs.
   
   ### Why startup fails in the first place is worth recording too
   
   The appender cannot bootstrap the keyspace it needs from the same JVM, 
because its own driver initialises Log4j. The DataStax driver depends on Netty, 
and Netty's `InternalLoggerFactory` acquires a Log4j logger *during driver 
initialisation*. That first `LogManager.getLogger` call triggers configuration, 
which starts the Cassandra appender, which calls `cluster.connect(keyspace)` — 
for a keyspace that any in-JVM bootstrap code has not created yet, because it 
is the very code initialising the driver:
   
   ```
   at 
org.apache.logging.log4j.cassandra.CassandraManager.startupInternal(CassandraManager.java:78)
   at 
org.apache.logging.log4j.core.appender.db.AbstractDatabaseManager.startup(AbstractDatabaseManager.java:259)
   at 
org.apache.logging.log4j.core.appender.db.AbstractDatabaseAppender.start(AbstractDatabaseAppender.java:183)
   at 
org.apache.logging.log4j.core.config.AbstractConfiguration.start(AbstractConfiguration.java:344)
   at 
org.apache.logging.log4j.core.LoggerContext.setConfiguration(LoggerContext.java:697)
   ...
   at org.apache.logging.log4j.LogManager.getLogger(LogManager.java:624)
   at 
io.netty.util.internal.logging.Log4J2LoggerFactory.newInstance(Log4J2LoggerFactory.java:33)
   at 
io.netty.util.internal.logging.InternalLoggerFactory.useLog4J2LoggerFactory(InternalLoggerFactory.java:76)
   ```
   
   `startupInternal()` runs once, so the appender is dead for the rest of the 
run. Creating the schema outside the JVM, before anything touches the driver, 
is the only thing that works — and with the keyspace pre-created the appender 
starts, writes and reads back correctly, which is what confirms the diagnosis 
rather than working around it. This may be unavoidable given the driver, but it 
is worth a line in the `log4j-cassandra` documentation, since the failure is 
silent and the natural fix (create the keyspace at startup) cannot work.
   
   ## Configuration
   
   **Version:** 2.x @ `04c93c1d33` (`log4j-cassandra` is not present on `main`, 
so this is 2.x-only)
   
   **Operating system:** macOS 15 (Darwin 25.5.0)
   
   **JDK:** Temurin 21
   
   **Cassandra:** 4.1.11 (driver 
`com.datastax.cassandra:cassandra-driver-core:3.11.x` as shipped)
   
   ## Logs
   
   ```
   main ERROR CassandraManager Cassandra Could not perform database startup 
operations:
     com.datastax.driver.core.exceptions.InvalidQueryException: Keyspace 
'log4j' does not exist
   
   main ERROR Unable to write to database [Cassandra] for appender [Cassandra].
   java.lang.NullPointerException: Cannot invoke 
"com.datastax.driver.core.PreparedStatement.bind(Object[])"
     because "this.preparedStatement" is null
       (repeated once per logged event)
   
   main DEBUG Shut down CassandraManager Cassandra, all resources released: true
       (JVM then does not exit)
   ```
   
   ## Reproduction
   
   1. Start Cassandra 4.1 and do **not** create the keyspace.
   2. Configure a `Cassandra` appender against it:
   
   ```xml
   <Cassandra name="Cassandra" clusterName="log4j-bench" keyspace="log4j"
              table="log_events" bufferSize="0" batched="false">
     <SocketAddress host="localhost" port="9042"/>
     <ColumnMapping name="id" pattern="%uuid{TIME}" type="java.util.UUID"/>
     <ColumnMapping name="message" pattern="%message"/>
     <ColumnMapping name="level" pattern="%level"/>
   </Cassandra>
   ```
   
   3. Log one event, call `LogManager.shutdown()`, and return from `main`.
   
   Expected: the JVM exits. Actual: it hangs; `jstack` shows the non-daemon 
driver threads above. Run with `-Dlog4j2.debug=true` to see the startup failure 
that the per-event NPEs otherwise bury.
   
   ## Suggested fix
   
   - Close the `Cluster` when `startupInternal()` fails — either by wrapping 
the body in a `try`/`catch` that calls `cluster.close()` and rethrows, or by 
making `shutdownInternal()` null-tolerant (`if (session != null) 
session.close(); if (cluster != null) cluster.close();`) and letting it run 
irrespective of `running`, per #4241.
   - Optionally note in the `log4j-cassandra` docs that the keyspace and table 
must exist before the JVM starts, and why in-JVM creation cannot work.
   
   Happy to put a PR together if the approach looks right.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to