Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
apiVersion=5.12.14
defaultVersion=5.12-SNAPSHOT

slf4jVersion = 1.7.36
slf4jVersion = 2.0.17

terracottaApisVersion = 1.9.1
tripwireVersion = 1.0.6
mockitoVersion = 5.14.2
junitVersion = 4.13.1
hamcrestVersion = 1.3
commonsIOVersion = 2.7
logbackVersion = 1.2.13
logbackVersion = 1.5.18

org.gradle.parallel=true
compileVM=17
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
public class BootstrapConfigurator extends ContextAwareBase implements Configurator {

@Override
public void configure(LoggerContext loggerContext) {
public ExecutionStatus configure(LoggerContext loggerContext) {
ch.qos.logback.classic.Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME);

BufferingAppender appender = new BufferingAppender();
Expand All @@ -56,5 +56,6 @@ public void configure(LoggerContext loggerContext) {
root.setLevel(Level.INFO);
loggerContext.start();
}
return ExecutionStatus.INVOKE_NEXT_IF_ANY;
}
}
18 changes: 15 additions & 3 deletions tc-server/src/main/java/com/tc/l2/logging/TCLogbackLogging.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,12 @@ public static void resetLogging() {
root.detachAndStopAllAppenders();
console.detachAndStopAllAppenders();
loggerContext.reset();

try {
new ch.qos.logback.classic.util.ContextInitializer(loggerContext).autoConfig();
} catch (Exception e) {
ch.qos.logback.core.util.StatusPrinter.printInCaseOfErrorsOrWarnings(loggerContext);
}
Comment on lines +54 to +59
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is used for inline testing and is an attempt to shutdown everything for the current server/classloader. The next server/classloader should do this automatically.

}

public static void setServerName(String name) {
Expand Down Expand Up @@ -91,7 +97,7 @@ public static void bootstrapLogging(OutputStream out) {
appender.start();
root.addAppender(appender);
}

if (!hasJfr && EventAppender.isEnabled()) {
EventAppender events = new EventAppender();
events.setName("LogToJFR");
Expand All @@ -103,7 +109,7 @@ public static void bootstrapLogging(OutputStream out) {
ch.qos.logback.classic.Logger silent = loggerContext.getLogger(TCLogging.SILENT_LOGGER_NAME);
silent.setAdditive(false);
silent.setLevel(Level.OFF);

if (!loggerContext.isStarted()) {
root.setLevel(Level.INFO);
loggerContext.start();
Expand All @@ -122,13 +128,19 @@ public static void redirectLogging(File logDirFile) {
} else {
disableBufferingAppender(null);
}

Appender<ILoggingEvent> bootstrap = root.getAppender("TC_BASE");
if (bootstrap != null) {
root.detachAppender(bootstrap);
try { bootstrap.stop(); } catch (Exception ignore) {}
}
Comment on lines +132 to +136
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We still want console output going to the output stream provided to bootstrap logging.

}

private static void disableBufferingAppender(Appender<ILoggingEvent> continuingAppender) {
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
ch.qos.logback.classic.Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME);
ch.qos.logback.classic.Logger console = loggerContext.getLogger(CONSOLE);

Iterator<Appender<ILoggingEvent>> appenders = root.iteratorForAppenders();
if (appenders != null) {
while (appenders.hasNext()) {
Expand Down
32 changes: 32 additions & 0 deletions tc-server/src/main/resources/logback.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright Terracotta, Inc.
~ Copyright IBM Corp. 2024, 2025
~
~ 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.
-->
<configuration>

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d [%t] %p %c - %m%n</pattern>
</encoder>
</appender>

<logger name="org.terracotta.console" level="INFO"/>

<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
</configuration>

97 changes: 66 additions & 31 deletions tc-server/src/test/java/com/tc/l2/logging/TCLogbackLoggingTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,21 @@

import static com.tc.l2.logging.TCLogbackLogging.CONSOLE;
import java.io.File;
import java.io.FileReader;
import java.io.LineNumberReader;
import java.nio.file.Files;
import java.util.Arrays;
import static org.hamcrest.Matchers.contains;

import static junit.framework.TestCase.assertTrue;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.LoggerContext;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;
import org.junit.Rule;
import org.junit.contrib.java.lang.system.SystemOutRule;
import org.junit.rules.TemporaryFolder;
Expand All @@ -43,7 +45,7 @@
*/
public class TCLogbackLoggingTest {

public @Rule SystemOutRule sysout = new SystemOutRule().enableLog();
public @Rule SystemOutRule systemOutRule = new SystemOutRule().enableLog();
public @Rule TemporaryFolder temp = new TemporaryFolder();

public TCLogbackLoggingTest() {
Expand All @@ -63,6 +65,7 @@ public void setUp() {

@After
public void tearDown() {
TCLogbackLogging.resetLogging();
}

/**
Expand All @@ -76,17 +79,24 @@ public void testBootstrapLogging() throws Exception {

// test that console logger is properly installed
Logger test = LoggerFactory.getLogger(CONSOLE);
systemOutRule.clearLog();
test.info("this is a test");
assertThat(sysout.getLog(), not(containsString("this is a test")));
File f = temp.newFolder();
TCLogbackLogging.redirectLogging(f);
assertThat(sysout.getLog(), containsString("this is a test"));
sysout.clearLog();
assertThat(sysout.getLog(), not(containsString("this is a test")));
System.out.println("PRINTING " + f.listFiles()[0].toPath());
Files.readAllLines(f.listFiles()[0].toPath()).forEach(System.out::println);
System.out.println("FINISHED " + f.listFiles()[0].toPath());
assertThat(Files.readAllLines(f.listFiles()[0].toPath()), contains(Arrays.asList(containsString("this is a test"), containsString("Log file:"))));

// redirect logging so buffered lines reach the console and the file
File logDir = temp.newFolder();
systemOutRule.clearLog();
TCLogbackLogging.redirectLogging(logDir);

String consoleLog = systemOutRule.getLog();
assertThat("Buffered message should hit the console once logging is redirected",
consoleLog, containsString("this is a test"));
assertThat("Console should announce redirection target", consoleLog, containsString("Log file:"));

File logFile = new File(logDir, "terracotta.server.log");
assertTrue("Log file should exist after redirect", logFile.exists());
String fileContents = Files.readString(logFile.toPath());
assertThat("Buffered message should be written to the log file",
fileContents, containsString("this is a test"));
}

/**
Expand All @@ -100,30 +110,55 @@ public void testRedirectLogging() throws Exception {

// test that console logger is properly installed
Logger test = LoggerFactory.getLogger(CONSOLE);
systemOutRule.clearLog();
test.info("this is a test");
assertThat(sysout.getLog(), not(containsString("this is a test")));

File folder = temp.newFolder();
systemOutRule.clearLog();
TCLogbackLogging.redirectLogging(folder);
assertThat(sysout.getLog(), containsString("this is a test"));
assertThat(systemOutRule.getLog(), containsString("this is a test"));
LoggerFactory.getLogger(CONSOLE).info("flush1");
LoggerFactory.getLogger(CONSOLE).info("flush2");
LoggerFactory.getLogger(CONSOLE).info("flush3");
LoggerFactory.getLogger(CONSOLE).info("flush4");

FileReader read = new FileReader(new File(folder, "terracotta.server.log"));
LineNumberReader lines = new LineNumberReader(read);
boolean contains = false;
String line = lines.readLine();
while (line != null) {
System.out.println("TESTING " + line);
if (line.contains("this is a test")) {
contains = true;
break;
}
line = lines.readLine();
}
assertTrue(contains);
File logFile = new File(folder, "terracotta.server.log");
assertTrue("Log file should exist after redirect", logFile.exists());
String fileContents = Files.readString(logFile.toPath());
assertTrue(fileContents.contains("this is a test"));
}

@Test
public void testResetLoggingForcesInfoLevel() {
TCLogbackLogging.resetLogging();

LoggerContext ctx = (LoggerContext) LoggerFactory.getILoggerFactory();
ch.qos.logback.classic.Logger console = ctx.getLogger(TCLogbackLogging.CONSOLE);

assertThat(console.getEffectiveLevel(), is(Level.INFO));
}

@Test
public void testBootstrapLoggingKeepsRootAtInfo() throws Exception {
TCLogbackLogging.resetLogging();

TCLogbackLogging.bootstrapLogging(null); // console appender setup

// Redirect logging to a temporary folder to enable console output
File folder = temp.newFolder();
TCLogbackLogging.redirectLogging(folder);

LoggerContext ctx = (LoggerContext) LoggerFactory.getILoggerFactory();
ch.qos.logback.classic.Logger root = ctx.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);

assertThat(root.getLevel(), is(Level.INFO));

systemOutRule.clearLog();
LoggerFactory.getLogger(TCLogbackLogging.CONSOLE).debug("debug-should-NOT-appear");
LoggerFactory.getLogger(TCLogbackLogging.CONSOLE).info("info-should-appear");

String log = systemOutRule.getLog();
assertThat("DEBUG should be filtered", log, not(containsString("debug-should-NOT-appear")));
assertThat("INFO should be logged", log, containsString("info-should-appear"));
}
}