You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by da...@apache.org on 2026/10/06 17:11:07 UTC

(jackrabbit-oak) 01/01: OAK-12439: restart reconfigured oak-run logging contexts

This is an automated email from the ASF dual-hosted git repository.

rishabhdaim pushed a commit to branch issue/OAK-12439
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git

commit a15c37e80d855fa9ef79adebc138e9a885c28c39
Author: rishabhdaim <ri...@gmail.com>
AuthorDate: Tue Oct 6 22:40:33 2026 +0530

    OAK-12439: restart reconfigured oak-run logging contexts
---
 .../oak/run/commons/LoggingInitializer.java        |   3 +
 .../oak/run/commons/LoggingInitializerTest.java    | 161 +++++++++++++++++++++
 .../src/test/resources/logback-lifecycle.xml       |  27 ++++
 3 files changed, 191 insertions(+)

diff --git a/oak-run-commons/src/main/java/org/apache/jackrabbit/oak/run/commons/LoggingInitializer.java b/oak-run-commons/src/main/java/org/apache/jackrabbit/oak/run/commons/LoggingInitializer.java
index e3239fe808..845c792d6d 100644
--- a/oak-run-commons/src/main/java/org/apache/jackrabbit/oak/run/commons/LoggingInitializer.java
+++ b/oak-run-commons/src/main/java/org/apache/jackrabbit/oak/run/commons/LoggingInitializer.java
@@ -100,6 +100,9 @@ public class LoggingInitializer {
                 context.reset();
             }
             configurator.doConfigure(config);
+            if (!context.isStarted()) {
+                context.start();
+            }
         } catch (JoranException je) {
             // StatusPrinter will handle this
         }
diff --git a/oak-run-commons/src/test/java/org/apache/jackrabbit/oak/run/commons/LoggingInitializerTest.java b/oak-run-commons/src/test/java/org/apache/jackrabbit/oak/run/commons/LoggingInitializerTest.java
new file mode 100644
index 0000000000..478e12cae8
--- /dev/null
+++ b/oak-run-commons/src/test/java/org/apache/jackrabbit/oak/run/commons/LoggingInitializerTest.java
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.jackrabbit.oak.run.commons;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ScheduledFuture;
+
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.joran.JoranConfigurator;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.classic.util.ContextInitializer;
+import ch.qos.logback.core.Appender;
+import ch.qos.logback.core.model.Model;
+import ch.qos.logback.core.model.ModelUtil;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Tests for {@link LoggingInitializer}.
+ */
+public class LoggingInitializerTest {
+    @Rule
+    public final TemporaryFolder temporaryFolder = new TemporaryFolder(new File("target"));
+
+    private LoggerContext context;
+    private Model originalConfiguration;
+    private boolean originallyStarted;
+    private String originalWorkDir;
+    private String originalConfigFile;
+
+    @Before
+    public void saveLoggingConfiguration() {
+        context = (LoggerContext) LoggerFactory.getILoggerFactory();
+        originallyStarted = context.isStarted();
+        JoranConfigurator configurator = new JoranConfigurator();
+        configurator.setContext(context);
+        originalConfiguration = configurator.recallSafeConfiguration();
+        originalWorkDir = System.getProperty("oak.workDir");
+        originalConfigFile = System.getProperty(ContextInitializer.CONFIG_FILE_PROPERTY);
+        System.clearProperty(ContextInitializer.CONFIG_FILE_PROPERTY);
+        context.reset();
+        context.start();
+    }
+
+    @After
+    public void restoreLoggingConfiguration() throws Exception {
+        // reset also cancels scanners if a failed initialization left the context stopped.
+        context.reset();
+        restoreProperty("oak.workDir", originalWorkDir);
+        restoreProperty(ContextInitializer.CONFIG_FILE_PROPERTY, originalConfigFile);
+        if (originalConfiguration != null) {
+            ModelUtil.resetForReuse(originalConfiguration);
+            JoranConfigurator configurator = new JoranConfigurator();
+            configurator.setContext(context);
+            configurator.processModel(originalConfiguration);
+            configurator.registerSafeConfiguration(originalConfiguration);
+        } else {
+            new ContextInitializer(context).autoConfig();
+        }
+        context.start();
+        if (!originallyStarted) {
+            context.stop();
+        }
+    }
+
+    @Test
+    public void reinitializationStartsStoppedContext() throws Exception {
+        new LoggingInitializer(temporaryFolder.newFolder(), "lifecycle").init();
+        LoggingInitializer.shutdownLogging();
+        Assert.assertFalse(context.isStarted());
+
+        new LoggingInitializer(temporaryFolder.newFolder(), "lifecycle").init();
+        Assert.assertTrue(context.isStarted());
+    }
+
+    @Test
+    public void repeatedShutdownStopsAppendersAndScanners() throws Exception {
+        for (int i = 0; i < 2; i++) {
+            File workDir = temporaryFolder.newFolder();
+            new LoggingInitializer(workDir, "lifecycle").init();
+            Appender<ILoggingEvent> appender = context.getLogger(Logger.ROOT_LOGGER_NAME).getAppender("file");
+            Assert.assertNotNull(appender);
+            Assert.assertTrue(appender.isStarted());
+            Assert.assertTrue(new File(workDir, "lifecycle.log").isFile());
+            List<ScheduledFuture<?>> scanners = new ArrayList<>(context.getCopyOfScheduledFutures());
+            Assert.assertFalse(scanners.isEmpty());
+
+            LoggingInitializer.shutdownLogging();
+
+            Assert.assertFalse("shutdown must close the file appender", appender.isStarted());
+            for (ScheduledFuture<?> scanner : scanners) {
+                Assert.assertTrue("shutdown must cancel configuration scanning", scanner.isCancelled());
+            }
+            Assert.assertFalse(context.isStarted());
+        }
+    }
+
+    @Test
+    public void initializationWithoutResetStartsStoppedContext() throws Exception {
+        context.stop();
+        new LoggingInitializer(temporaryFolder.newFolder(), "lifecycle", false).init();
+        Assert.assertTrue(context.isStarted());
+        LoggingInitializer.shutdownLogging();
+        Assert.assertTrue(context.getCopyOfScheduledFutures().isEmpty());
+        Assert.assertFalse(context.isStarted());
+    }
+
+    @Test
+    public void customConfigurationSkipsInitializationAndShutdown() throws Exception {
+        File workDir = temporaryFolder.newFolder();
+        new LoggingInitializer(workDir, "lifecycle").init();
+        Appender<ILoggingEvent> appender = context.getLogger(Logger.ROOT_LOGGER_NAME).getAppender("file");
+        List<ScheduledFuture<?>> scanners = new ArrayList<>(context.getCopyOfScheduledFutures());
+        Assert.assertFalse(scanners.isEmpty());
+        String configuredWorkDir = System.getProperty("oak.workDir");
+        System.setProperty(ContextInitializer.CONFIG_FILE_PROPERTY, new File(workDir, "logback-lifecycle.xml").getAbsolutePath());
+
+        File otherWorkDir = temporaryFolder.newFolder();
+        new LoggingInitializer(otherWorkDir, "lifecycle").init();
+        LoggingInitializer.shutdownLogging();
+
+        Assert.assertFalse(new File(otherWorkDir, "logback-lifecycle.xml").exists());
+        Assert.assertEquals(configuredWorkDir, System.getProperty("oak.workDir"));
+        Assert.assertSame(appender, context.getLogger(Logger.ROOT_LOGGER_NAME).getAppender("file"));
+        Assert.assertTrue(context.isStarted());
+        Assert.assertTrue(appender.isStarted());
+        for (ScheduledFuture<?> scanner : scanners) {
+            Assert.assertFalse(scanner.isCancelled());
+        }
+    }
+
+    private static void restoreProperty(String name, String value) {
+        if (value == null) {
+            System.clearProperty(name);
+        } else {
+            System.setProperty(name, value);
+        }
+    }
+}
diff --git a/oak-run-commons/src/test/resources/logback-lifecycle.xml b/oak-run-commons/src/test/resources/logback-lifecycle.xml
new file mode 100644
index 0000000000..29183cded1
--- /dev/null
+++ b/oak-run-commons/src/test/resources/logback-lifecycle.xml
@@ -0,0 +1,27 @@
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You 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.
+-->
+<configuration scan="true" scanPeriod="1 hour">
+    <appender name="file" class="ch.qos.logback.core.FileAppender">
+        <file>${oak.workDir}/lifecycle.log</file>
+        <encoder>
+            <pattern>%msg%n</pattern>
+        </encoder>
+    </appender>
+    <root level="INFO">
+        <appender-ref ref="file"/>
+    </root>
+</configuration>