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

sk0x50 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new e32fd8d092 IGNITE 19148 Switch tests from using JUL to log4j2 (#2429)
e32fd8d092 is described below

commit e32fd8d092ed1a3074013dcd2cb64f2b1b50ab18
Author: Slava Koptilin <slava.kopti...@gmail.com>
AuthorDate: Mon Aug 21 13:48:41 2023 +0300

    IGNITE 19148 Switch tests from using JUL to log4j2 (#2429)
---
 build.gradle                                       |   1 -
 buildscripts/java-integration-test.gradle          |   5 +
 buildscripts/java-junit5.gradle                    |   5 +
 buildscripts/java-test-fixtures.gradle             |   5 +
 config/java.util.logging.properties                |  57 ----
 gradle/libs.versions.toml                          |   7 +
 modules/catalog/build.gradle                       |   1 -
 .../cli/commands/ItClusterCommandTest.java         |  44 +--
 modules/client-handler/build.gradle                |   1 -
 modules/cluster-management/build.gradle            |   1 -
 modules/configuration-presentation/build.gradle    |   1 -
 .../testframework/BaseIgniteAbstractTest.java      |  14 -
 .../internal/testframework/jul/NoOpHandler.java    |  36 --
 .../testframework/log4j2/LogInspector.java         | 364 +++++++++++++++++++++
 .../resources/java.util.logging.properties         |  57 ----
 .../src/testFixtures/resources/log4j2-test.xml     |  58 ++++
 modules/index/build.gradle                         |   1 -
 modules/network/build.gradle                       |   3 +-
 .../scalecube/ItScaleCubeNetworkMessagingTest.java |  36 +-
 modules/raft/build.gradle                          |   1 -
 modules/rest/build.gradle                          |   1 -
 modules/runner/build.gradle                        |   1 -
 .../raftsnapshot/ItTableRaftSnapshotsTest.java     |  69 ++--
 .../apache/ignite/internal/start/ItStartTest.java  |  42 ++-
 modules/sql-engine/build.gradle                    |   1 -
 modules/table/build.gradle                         |   1 -
 26 files changed, 529 insertions(+), 284 deletions(-)

diff --git a/build.gradle b/build.gradle
index 974560658a..bd9d33f11f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -67,7 +67,6 @@ allprojects {
                     "--add-opens=java.base/jdk.internal.misc=ALL-UNNAMED",
                     "--add-opens=java.base/sun.security.x509=ALL-UNNAMED",
                     "-Dio.netty.tryReflectionSetAccessible=true",
-                    
"-Djava.util.logging.config.file=${project.rootDir}/config/java.util.logging.properties",
                     "-XX:+HeapDumpOnOutOfMemoryError"]
 
         systemProperty 'jraft.available_processors', 1
diff --git a/buildscripts/java-integration-test.gradle 
b/buildscripts/java-integration-test.gradle
index 173598386d..b683fdb3db 100644
--- a/buildscripts/java-integration-test.gradle
+++ b/buildscripts/java-integration-test.gradle
@@ -35,6 +35,11 @@ testing {
                 implementation libs.hamcrest.core
                 implementation libs.hamcrest.optional
                 implementation libs.hamcrest.path
+
+                implementation libs.jansi.core
+                implementation libs.log4j.api
+                implementation libs.log4j.core
+                implementation libs.log4j.bridge
             }
 
             sources {
diff --git a/buildscripts/java-junit5.gradle b/buildscripts/java-junit5.gradle
index f233c06084..f29fc1d6fa 100644
--- a/buildscripts/java-junit5.gradle
+++ b/buildscripts/java-junit5.gradle
@@ -33,4 +33,9 @@ dependencies {
     testImplementation libs.junit5.impl
     testImplementation libs.junit5.api
     testImplementation libs.junit5.params
+
+    testImplementation libs.jansi.core
+    testImplementation libs.log4j.api
+    testImplementation libs.log4j.core
+    testImplementation libs.log4j.bridge
 }
diff --git a/buildscripts/java-test-fixtures.gradle 
b/buildscripts/java-test-fixtures.gradle
index 49ec0f1f60..17feb22e60 100644
--- a/buildscripts/java-test-fixtures.gradle
+++ b/buildscripts/java-test-fixtures.gradle
@@ -20,6 +20,11 @@ apply plugin: 'java-test-fixtures'
 dependencies {
     testFixturesImplementation libs.junit5.api
     testFixturesImplementation libs.junit5.impl
+
+    testFixturesImplementation libs.jansi.core
+    testFixturesImplementation libs.log4j.api
+    testFixturesImplementation libs.log4j.core
+    testFixturesImplementation libs.log4j.bridge
 }
 
 pmdTestFixtures {
diff --git a/config/java.util.logging.properties 
b/config/java.util.logging.properties
deleted file mode 100644
index 9a4ef63f5f..0000000000
--- a/config/java.util.logging.properties
+++ /dev/null
@@ -1,57 +0,0 @@
-#
-# 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.
-#
-
-#########################################################################
-#       Default java.util.logging configuration for Ignite.
-#
-# To use another config file use `java.util.logging.config.file` system
-# property. For example `java -Djava.util.logging.config.file=myfile`
-#########################################################################
-
-#
-# Comma-separated list of logging "handlers". Note that some of them may be
-# reconfigured (or even removed) at runtime according to system properties.
-#
-# By default all messages will be passed to console and file.
-#
-handlers=java.util.logging.ConsoleHandler, java.util.logging.FileHandler
-
-#
-# Default global logging level.
-# This specifies which kinds of events are logged across all loggers.
-# For any given category this global level can be overriden by a category
-# specific level.
-# Note that handlers also have a separate level setting to limit messages
-# printed through it.
-#
-.level=INFO
-
-# Console handler logs all messages with importance level `INFO` and above
-# into standard error stream (`System.err`).
-#
-java.util.logging.ConsoleHandler.formatter = 
org.apache.ignite.lang.JavaLoggerFormatter
-java.util.logging.ConsoleHandler.level = INFO
-
-#
-# File handler logs all messages into files with pattern `ignite-%g.log`
-# under `build` directory.
-#
-java.util.logging.FileHandler.formatter = 
org.apache.ignite.lang.JavaLoggerFormatter
-java.util.logging.FileHandler.pattern = build/ignite-%g.log
-java.util.logging.FileHandler.level = INFO
-java.util.logging.FileHandler.limit = 10485760
-java.util.logging.FileHandler.count = 10
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 089e0e4176..0484a1ee7c 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -44,6 +44,7 @@ micronautReactor="2.5.0"
 mockito = "5.4.0"
 picocli = "4.7.0"
 slf4j = "2.0.7"
+log4j = "2.19.0"
 spoon = "8.4.0-beta-18"
 swagger = "2.2.8"
 swaggerLegacy = "1.6.4"
@@ -153,7 +154,13 @@ jackson-databind = { module = 
"com.fasterxml.jackson.core:jackson-databind", ver
 jackson-annotations = { module = 
"com.fasterxml.jackson.core:jackson-annotations", version.ref = "jackson" }
 
 typesafe-config = { module = "com.typesafe:config", version.ref = "typesafe" }
+
 slf4j-jdk14 = { module = "org.slf4j:slf4j-jdk14", version.ref = "slf4j" }
+slf4j-log4j = { module = "org.apache.logging.log4j:log4j-slf4j2-impl", 
version.ref = "log4j" }
+
+log4j-api = { module = "org.apache.logging.log4j:log4j-api", version.ref = 
"log4j" }
+log4j-core = { module = "org.apache.logging.log4j:log4j-core", version.ref = 
"log4j" }
+log4j-bridge = { module = "org.apache.logging.log4j:log4j-jpl", version.ref = 
"log4j" }
 
 gson-core = { module = "com.google.code.gson:gson", version.ref = "gson" }
 gson-fire = { module = "io.gsonfire:gson-fire", version.ref = "gsonFire" }
diff --git a/modules/catalog/build.gradle b/modules/catalog/build.gradle
index 43b2579ee7..ab97262744 100644
--- a/modules/catalog/build.gradle
+++ b/modules/catalog/build.gradle
@@ -44,7 +44,6 @@ dependencies {
     testImplementation libs.mockito.junit
     testImplementation libs.mockito.core
     testImplementation libs.hamcrest.core
-    testImplementation libs.slf4j.jdk14
 
     testFixturesImplementation libs.jetbrains.annotations
     testFixturesImplementation libs.mockito.core
diff --git 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/ItClusterCommandTest.java
 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/ItClusterCommandTest.java
index 24a5c5f998..ad34c566ba 100644
--- 
a/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/ItClusterCommandTest.java
+++ 
b/modules/cli/src/integrationTest/java/org/apache/ignite/internal/cli/commands/ItClusterCommandTest.java
@@ -34,15 +34,12 @@ import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
-import java.util.logging.Handler;
-import java.util.logging.LogRecord;
-import java.util.logging.Logger;
 import org.apache.ignite.IgnitionManager;
 import org.apache.ignite.internal.cli.AbstractCliTest;
 import org.apache.ignite.internal.testframework.TestIgnitionManager;
 import org.apache.ignite.internal.testframework.WorkDirectory;
 import org.apache.ignite.internal.testframework.WorkDirectoryExtension;
-import org.apache.ignite.internal.testframework.jul.NoOpHandler;
+import org.apache.ignite.internal.testframework.log4j2.LogInspector;
 import org.hamcrest.Matcher;
 import org.hamcrest.Matchers;
 import org.junit.jupiter.api.AfterEach;
@@ -70,42 +67,35 @@ class ItClusterCommandTest extends AbstractCliTest {
 
     private static final String NL = System.lineSeparator();
 
-    private static final Logger topologyLogger = 
Logger.getLogger("org.apache.ignite.network.scalecube.ScaleCubeTopologyService");
-
     @BeforeEach
     void setup(@WorkDirectory Path workDir, TestInfo testInfo) throws 
Exception {
         CountDownLatch allNodesAreInPhysicalTopology = new CountDownLatch(1);
 
-        Handler physicalTopologyWaiter = 
physicalTopologyWaiter(allNodesAreInPhysicalTopology);
-        topologyLogger.addHandler(physicalTopologyWaiter);
+        LogInspector topologyLogInspector = new LogInspector(
+                "org.apache.ignite.network.scalecube.ScaleCubeTopologyService",
+                evt -> {
+                    String msg = evt.getMessage().getFormattedMessage();
+                    if (msg.startsWith(TOPOLOGY_SNAPSHOT_LOG_RECORD_PREFIX)) {
+                        var ids = 
msg.substring(TOPOLOGY_SNAPSHOT_LOG_RECORD_PREFIX.length(), 
msg.lastIndexOf(']'))
+                                .split(",");
+
+                        return ids.length == NODES.size();
+                    }
+                    return false;
+                },
+                allNodesAreInPhysicalTopology::countDown);
+
+        topologyLogInspector.start();
 
         try {
             startClusterWithoutInit(workDir, testInfo);
 
             
waitTillAllNodesJoinPhysicalTopology(allNodesAreInPhysicalTopology);
         } finally {
-            topologyLogger.removeHandler(physicalTopologyWaiter);
+            topologyLogInspector.stop();
         }
     }
 
-    private Handler physicalTopologyWaiter(CountDownLatch 
physicalTopologyIsFull) {
-        return new NoOpHandler() {
-            @Override
-            public void publish(LogRecord record) {
-                var msg = record.getMessage();
-
-                if (msg.startsWith(TOPOLOGY_SNAPSHOT_LOG_RECORD_PREFIX)) {
-                    var ids = 
msg.substring(TOPOLOGY_SNAPSHOT_LOG_RECORD_PREFIX.length(), 
msg.lastIndexOf(']'))
-                            .split(",");
-
-                    if (ids.length == NODES.size()) {
-                        physicalTopologyIsFull.countDown();
-                    }
-                }
-            }
-        };
-    }
-
     private void startClusterWithoutInit(Path workDir, TestInfo testInfo) {
         NODES.parallelStream().forEach(node -> startNodeWithoutInit(node, 
workDir, testInfo));
     }
diff --git a/modules/client-handler/build.gradle 
b/modules/client-handler/build.gradle
index 17907c6f81..cc1332637e 100644
--- a/modules/client-handler/build.gradle
+++ b/modules/client-handler/build.gradle
@@ -38,7 +38,6 @@ dependencies {
     implementation project(':ignite-transactions')
     implementation libs.jetbrains.annotations
     implementation libs.fastutil.core
-    implementation libs.slf4j.jdk14
     implementation libs.netty.common
     implementation libs.netty.buffer
     implementation libs.netty.codec
diff --git a/modules/cluster-management/build.gradle 
b/modules/cluster-management/build.gradle
index af1e25bf62..071979b27a 100644
--- a/modules/cluster-management/build.gradle
+++ b/modules/cluster-management/build.gradle
@@ -50,7 +50,6 @@ dependencies {
     testImplementation project(':ignite-network')
     testImplementation libs.hamcrest.core
     testImplementation libs.mockito.junit
-    testImplementation libs.slf4j.jdk14
 
     testFixturesImplementation project(':ignite-core')
     testFixturesImplementation project(':ignite-raft')
diff --git a/modules/configuration-presentation/build.gradle 
b/modules/configuration-presentation/build.gradle
index e0b90d4a33..9efd2495b2 100644
--- a/modules/configuration-presentation/build.gradle
+++ b/modules/configuration-presentation/build.gradle
@@ -34,7 +34,6 @@ dependencies {
     testImplementation project(':ignite-configuration')
     testImplementation testFixtures(project(':ignite-core'))
     testImplementation testFixtures(project(':ignite-configuration'))
-    testImplementation libs.slf4j.jdk14
     testImplementation libs.micronaut.junit5
     testImplementation libs.micronaut.http.client
     testImplementation libs.mockito.core
diff --git 
a/modules/core/src/testFixtures/java/org/apache/ignite/internal/testframework/BaseIgniteAbstractTest.java
 
b/modules/core/src/testFixtures/java/org/apache/ignite/internal/testframework/BaseIgniteAbstractTest.java
index fe2aaa1091..b650ee641f 100644
--- 
a/modules/core/src/testFixtures/java/org/apache/ignite/internal/testframework/BaseIgniteAbstractTest.java
+++ 
b/modules/core/src/testFixtures/java/org/apache/ignite/internal/testframework/BaseIgniteAbstractTest.java
@@ -22,11 +22,8 @@ import static 
org.apache.ignite.lang.IgniteSystemProperties.IGNITE_SENSITIVE_DAT
 import static org.apache.ignite.lang.IgniteSystemProperties.getString;
 import static org.mockito.Mockito.framework;
 
-import java.io.IOException;
-import java.io.InputStream;
 import java.lang.reflect.Method;
 import java.nio.file.Path;
-import java.util.logging.LogManager;
 import org.apache.ignite.internal.logger.IgniteLogger;
 import org.apache.ignite.internal.logger.Loggers;
 import org.apache.ignite.internal.tostring.S;
@@ -47,18 +44,7 @@ public abstract class BaseIgniteAbstractTest {
     /** Test start time in milliseconds. */
     private long testStartMs;
 
-    //
-    // IDEA test runner didn't apply provided from Gradle scripts 
-Djava.util.logging.config.file
-    // So, we need to apply logging properties manually to {@link LogManager}.
-    //
     static {
-        try (InputStream is = BaseIgniteAbstractTest.class.getClassLoader()
-                        .getResourceAsStream("java.util.logging.properties")) {
-            LogManager.getLogManager().readConfiguration(is);
-        } catch (IOException e) {
-            throw new IllegalStateException("Logger properties not found.");
-        }
-
         /* Init test env. */
         S.setSensitiveDataLoggingPolicySupplier(() ->
                 
SensitiveDataLoggingPolicy.valueOf(getString(IGNITE_SENSITIVE_DATA_LOGGING, 
"hash").toUpperCase()));
diff --git 
a/modules/core/src/testFixtures/java/org/apache/ignite/internal/testframework/jul/NoOpHandler.java
 
b/modules/core/src/testFixtures/java/org/apache/ignite/internal/testframework/jul/NoOpHandler.java
deleted file mode 100644
index 91077381d9..0000000000
--- 
a/modules/core/src/testFixtures/java/org/apache/ignite/internal/testframework/jul/NoOpHandler.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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.ignite.internal.testframework.jul;
-
-import java.util.logging.Handler;
-import java.util.logging.LogRecord;
-
-/**
- * Adapter for {@link Handler} with empty implementations of all methods but 
{@link Handler#publish(LogRecord)}.
- */
-public abstract class NoOpHandler extends Handler {
-    @Override
-    public void flush() {
-        // no-op
-    }
-
-    @Override
-    public void close() throws SecurityException {
-        // no-op
-    }
-}
diff --git 
a/modules/core/src/testFixtures/java/org/apache/ignite/internal/testframework/log4j2/LogInspector.java
 
b/modules/core/src/testFixtures/java/org/apache/ignite/internal/testframework/log4j2/LogInspector.java
new file mode 100755
index 0000000000..fae0b21f81
--- /dev/null
+++ 
b/modules/core/src/testFixtures/java/org/apache/ignite/internal/testframework/log4j2/LogInspector.java
@@ -0,0 +1,364 @@
+/*
+ * 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.ignite.internal.testframework.log4j2;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.function.Predicate;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.core.Appender;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.Logger;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.LoggerConfig;
+import org.apache.logging.log4j.core.config.Property;
+
+/**
+ * This class is used to check log events/messages.
+ * <p>
+ * When it is needed to check log events/messages, the following steps should 
be performed:
+ * </p>
+ * <pre><code>
+ *     // Create a new instance of LogInspector for the given logger name and 
specified predicate.
+ *     LogInspector logInspector = new LogInspector(
+ *             CustomClass.class.getName(),
+ *             evt -> evt.getMessage().getFormattedMessage().matches(pattern) 
&& evt.getLevel() == Level.ERROR);
+ *
+ *     logInspector.start();
+ *     try {
+ *         // do something
+ *     } finally {
+ *         logInspector.stop();
+ *     }
+ *
+ *     assertThat(logInspector.isMatched(), is(true));
+ * </code></pre>
+ * <p>
+ * When it is needed to check log events/messages and perform some action, 
just add a predicate and an action:
+ * </p>
+ * <pre><code>
+ *     AtomicInteger messageCounter = new AtomicInteger();
+ *     LogInspector checker = new LogInspector(
+ *             CustomClass.class.getName(),
+ *             evt -> evt.getMessage().getFormattedMessage().matches(pattern) 
&& evt.getLevel() == Level.ERROR,
+ *             // This action will be executed when the predicate is matched.
+ *             () -> {
+ *                 messageCounter.incrementAndGet();
+ *             });
+ *
+ *     logInspector.start();
+ *     try {
+ *         // do something
+ *     } finally {
+ *         logInspector.stop();
+ *     }
+ *
+ *     assertThat(messageCounter.get(), is(42));
+ * </code></pre>
+ * <p>
+ * It is possible to add a new pair of predicate and action using {@link 
#addHandler(Predicate, Runnable)},
+ * {@link #addHandler(Handler)} methods at any time.
+ * </p>
+ */
+public class LogInspector {
+    /** Logger name. */
+    private final String loggerName;
+
+    /** List of handlers. */
+    private final List<Handler> handlers = new ArrayList<>(1);
+
+    /** Lock that is used to synchronize the collection of handlers. */
+    private final ReadWriteLock lock = new ReentrantReadWriteLock();
+
+    /** Test log appender that is used to check log events/messages. */
+    private final TestLogAppender appender;
+
+    /** Logger configuration. */
+    private Configuration config;
+
+    /**
+     * Creates a new instance of {@link LogInspector} for the given {@code 
loggerName}.
+     *
+     * @return New instance of {@link LogInspector}.
+     **/
+    public static LogInspector create(String loggerName) {
+        return create(loggerName, false);
+    }
+
+    /**
+     * Creates a new instance of {@link LogInspector} for the given {@code 
loggerName}.
+     *
+     * @param started Whether the checker should be started.
+     * @return New instance of {@link LogInspector}.
+     **/
+    public static LogInspector create(String loggerName, boolean started) {
+        LogInspector checker = new LogInspector(loggerName);
+
+        if (started) {
+            checker.start();
+        }
+
+        return checker;
+    }
+
+    /**
+     * Creates a new instance of {@link LogInspector} for the given {@code 
clazz}.
+     *
+     * @return New instance of {@link LogInspector}.
+     **/
+    public static LogInspector create(Class<?> clazz) {
+        return create(clazz, false);
+    }
+
+    /**
+     * Creates a new instance of {@link LogInspector} for the given {@code 
clazz}.
+     *
+     * @param started Whether the checker should be started.
+     * @return New instance of {@link LogInspector}.
+     **/
+    public static LogInspector create(Class<?> clazz, boolean started) {
+        return create(clazz.getName(), started);
+    }
+
+    /**
+     * Creates a new instance of {@link LogInspector}.
+     *
+     * @param loggerName Logger name.
+     */
+    public LogInspector(String loggerName) {
+        Objects.requireNonNull(loggerName);
+
+        this.loggerName = loggerName;
+        this.appender = new TestLogAppender(loggerName);
+    }
+
+    /**
+     * Creates a new instance of {@link LogInspector} with the given {@code 
predicate}.
+     * In order to check that the required log event was logged, use {@link 
#isMatched()} method.
+     *
+     * @param loggerName Logger name.
+     * @param predicate Predicate to check log messages.
+     */
+    public LogInspector(String loggerName, Predicate<LogEvent> predicate) {
+        this(loggerName, predicate, () -> {});
+    }
+
+    /**
+     * Creates a new instance of {@link LogInspector} with the given {@code 
predicate} and {@code action}.
+     *
+     * @param loggerName Logger name.
+     * @param predicate Predicate to check log messages.
+     * @param action Action to be executed when the {@code predicate} is 
matched.
+     */
+    public LogInspector(String loggerName, Predicate<LogEvent> predicate, 
Runnable action) {
+        this(loggerName, new Handler(predicate, action));
+    }
+
+    /**
+     * Creates a new instance of {@link LogInspector} with the given list of 
{@code handlers}.
+     *
+     * @param loggerName Logger name.
+     * @param handlers List of handlers to be added.
+     */
+    public LogInspector(String loggerName, Handler... handlers) {
+        this(loggerName);
+
+        lock.writeLock().lock();
+        try {
+            Collections.addAll(this.handlers, handlers);
+        } finally {
+            lock.writeLock().unlock();
+        }
+    }
+
+    /**
+     * Adds a new handler with the given {@code predicate} and {@code action}.
+     *
+     * @param predicate Predicate to check log messages.
+     * @param action Action to be executed when the {@code predicate} is 
matched.
+     * @return New instance of {@link Handler}.
+     */
+    public Handler addHandler(Predicate<LogEvent> predicate, Runnable action) {
+        Handler handler = new Handler(predicate, action);
+
+        addHandler(handler);
+
+        return handler;
+    }
+
+    /**
+     * Adds a new handler with the given {@code predicate} and {@code action}.
+     *
+     * @param handler Handler to be added.
+     */
+    public void addHandler(Handler handler) {
+        lock.writeLock().lock();
+        try {
+            handlers.add(handler);
+        } finally {
+            lock.writeLock().unlock();
+        }
+    }
+
+    /**
+     * Removes the given {@code handler}.
+     *
+     * @param handler Handler to be removed.
+     */
+    public void removeHandler(Handler handler) {
+        Objects.requireNonNull(handler);
+
+        lock.writeLock().lock();
+        try {
+            handlers.remove(handler);
+        } finally {
+            lock.writeLock().unlock();
+        }
+    }
+
+    /**
+     * Checks if any of the predicates is matched.
+     *
+     * @return {@code true} if one of the predicates is matched, {@code false} 
otherwise.
+     */
+    public boolean isMatched() {
+        lock.readLock().lock();
+
+        try {
+            return handlers.stream().anyMatch(handler -> 
handler.isMatched.get());
+        } finally {
+            lock.readLock().unlock();
+        }
+    }
+
+    /**
+     * Starts the test log checker.
+     *
+     * @throws IllegalStateException If the checker is already started.
+     */
+    public void start() {
+        if (config != null) {
+            throw new IllegalStateException("TestLogChecker is already 
started.");
+        }
+
+        Logger logger = (Logger) LogManager.getLogger(appender.getName());
+
+        config = logger.getContext().getConfiguration();
+
+        appender.start();
+
+        addAppender(appender, config);
+    }
+
+    /**
+     * Stops the test log checker.
+     *
+     * @throws IllegalStateException If the checker is not started.
+     */
+    public void stop() {
+        if (config == null) {
+            throw new IllegalStateException("TestLogChecker is not started.");
+        }
+
+        appender.stop();
+
+        removeAppender(appender, config);
+    }
+
+    private static synchronized void addAppender(Appender appender, 
Configuration config) {
+        for (LoggerConfig loggerConfig : config.getLoggers().values()) {
+            loggerConfig.addAppender(appender, null, null);
+        }
+        config.getRootLogger().addAppender(appender, null, null);
+    }
+
+    private static synchronized void removeAppender(Appender appender, 
Configuration config) {
+        for (LoggerConfig loggerConfig : config.getLoggers().values()) {
+            loggerConfig.removeAppender(appender.getName());
+        }
+        config.getRootLogger().removeAppender(appender.getName());
+    }
+
+    /**
+     * Log event handler.
+     */
+    public static class Handler {
+        /** Predicate that is used to check log messages. */
+        private final Predicate<LogEvent> predicate;
+
+        /** Action to be executed when the {@code predicate} is matched. */
+        private final Runnable action;
+
+        /** Flag indicating whether the predicate is matched. */
+        private final AtomicBoolean isMatched = new AtomicBoolean();
+
+        /**
+         * Creates a new instance of {@link Handler}.
+         *
+         * @param predicate Predicate to check log messages.
+         * @param action Action to be executed when the {@code predicate} is 
matched.
+         */
+        public Handler(Predicate<LogEvent> predicate, Runnable action) {
+            Objects.requireNonNull(predicate);
+            Objects.requireNonNull(action);
+
+            this.predicate = predicate;
+            this.action = action;
+        }
+
+        /**
+         * Checks if the predicate is matched.
+         *
+         * @return {@code true} if the predicate is matched, {@code false} 
otherwise.
+         */
+        public boolean isMatched() {
+            return isMatched.get();
+        }
+    }
+
+    private class TestLogAppender extends AbstractAppender {
+        public TestLogAppender(String name) {
+            super(name, null, null, true, Property.EMPTY_ARRAY);
+        }
+
+        @Override
+        public void append(LogEvent event) {
+            if (!loggerName.equals(event.getLoggerName())) {
+                return;
+            }
+
+            lock.readLock().lock();
+            try {
+                handlers.forEach(handler -> {
+                    if (handler.predicate.test(event)) {
+                        handler.isMatched.set(true);
+                        handler.action.run();
+                    }
+                });
+            } finally {
+                lock.readLock().unlock();
+            }
+        }
+    }
+}
diff --git 
a/modules/core/src/testFixtures/resources/java.util.logging.properties 
b/modules/core/src/testFixtures/resources/java.util.logging.properties
deleted file mode 100644
index 9a4ef63f5f..0000000000
--- a/modules/core/src/testFixtures/resources/java.util.logging.properties
+++ /dev/null
@@ -1,57 +0,0 @@
-#
-# 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.
-#
-
-#########################################################################
-#       Default java.util.logging configuration for Ignite.
-#
-# To use another config file use `java.util.logging.config.file` system
-# property. For example `java -Djava.util.logging.config.file=myfile`
-#########################################################################
-
-#
-# Comma-separated list of logging "handlers". Note that some of them may be
-# reconfigured (or even removed) at runtime according to system properties.
-#
-# By default all messages will be passed to console and file.
-#
-handlers=java.util.logging.ConsoleHandler, java.util.logging.FileHandler
-
-#
-# Default global logging level.
-# This specifies which kinds of events are logged across all loggers.
-# For any given category this global level can be overriden by a category
-# specific level.
-# Note that handlers also have a separate level setting to limit messages
-# printed through it.
-#
-.level=INFO
-
-# Console handler logs all messages with importance level `INFO` and above
-# into standard error stream (`System.err`).
-#
-java.util.logging.ConsoleHandler.formatter = 
org.apache.ignite.lang.JavaLoggerFormatter
-java.util.logging.ConsoleHandler.level = INFO
-
-#
-# File handler logs all messages into files with pattern `ignite-%g.log`
-# under `build` directory.
-#
-java.util.logging.FileHandler.formatter = 
org.apache.ignite.lang.JavaLoggerFormatter
-java.util.logging.FileHandler.pattern = build/ignite-%g.log
-java.util.logging.FileHandler.level = INFO
-java.util.logging.FileHandler.limit = 10485760
-java.util.logging.FileHandler.count = 10
diff --git a/modules/core/src/testFixtures/resources/log4j2-test.xml 
b/modules/core/src/testFixtures/resources/log4j2-test.xml
new file mode 100755
index 0000000000..3e07e837ed
--- /dev/null
+++ b/modules/core/src/testFixtures/resources/log4j2-test.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  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>
+  <Properties>
+    <!-- Set the `IGNITE_CI` environment variable to completely disable colors 
in the console output. -->
+    <Property name="disableAnsi">${env:IGNITE_CI:-false}</Property>
+  </Properties>
+
+  <Appenders>
+    <Console name="CONSOLE" target="SYSTEM_OUT">
+      <PatternLayout
+        pattern="[%d{ISO8601}]%highlight{[%-5p]}{INFO=green, DEBUG=green bold, 
TRACE=blue}%style{[%t]}{magenta}%style{[%c{1}]}{cyan}%notEmpty{[%markerSimpleName]}
 %m%n"
+        disableAnsi="${disableAnsi}"/>
+      <LevelRangeFilter minLevel="INFO" maxLevel="DEBUG"/>
+    </Console>
+
+    <Console name="CONSOLE_ERR" target="SYSTEM_ERR">
+      <PatternLayout
+        pattern="%highlight{[%d{ISO8601}][%-5p][%t][%c{1}] %m%n}{FATAL=red 
blink, ERROR=default, WARN=yellow bold}"
+        disableAnsi="${disableAnsi}"/>
+    </Console>
+
+    <RollingFile name="FILE"
+      append="true"
+      fileName="build/ignite.log"
+      filePattern="build/ignite.log.%i">
+      <PatternLayout 
pattern="[%d{ISO8601}][%-5p][%t][%c{1}]%notEmpty{[%markerSimpleName]} %m%n"/>
+      <Policies>
+        <SizeBasedTriggeringPolicy size="10 MB"/>
+      </Policies>
+      <DefaultRolloverStrategy max="10"/>
+    </RollingFile>
+  </Appenders>
+
+  <Loggers>
+    <Root level="INFO">
+      <AppenderRef ref="CONSOLE" level="DEBUG"/>
+      <AppenderRef ref="CONSOLE_ERR" level="WARN"/>
+      <AppenderRef ref="FILE" level="DEBUG"/>
+    </Root>
+  </Loggers>
+</Configuration>
diff --git a/modules/index/build.gradle b/modules/index/build.gradle
index 455be1b734..d7bf5fb91c 100644
--- a/modules/index/build.gradle
+++ b/modules/index/build.gradle
@@ -37,7 +37,6 @@ dependencies {
     testImplementation libs.mockito.core
     testImplementation libs.mockito.junit
     testImplementation libs.hamcrest.core
-    testImplementation libs.slf4j.jdk14
 }
 
 description = 'ignite-index'
diff --git a/modules/network/build.gradle b/modules/network/build.gradle
index fb33f0694c..9944c87007 100644
--- a/modules/network/build.gradle
+++ b/modules/network/build.gradle
@@ -60,10 +60,10 @@ dependencies {
     testImplementation libs.mockito.core
     testImplementation libs.mockito.junit
     testImplementation libs.javapoet
-    testImplementation libs.slf4j.jdk14
     testImplementation libs.bytebuddy
     testImplementation libs.compileTesting
     testImplementation libs.awaitility
+    testImplementation libs.slf4j.log4j
 
     testFixturesAnnotationProcessor 
project(":ignite-network-annotation-processor")
     testFixturesImplementation project(':ignite-configuration')
@@ -73,6 +73,7 @@ dependencies {
     testFixturesImplementation libs.fastutil.core
     testFixturesImplementation libs.scalecube.cluster
     testFixturesImplementation libs.jetbrains.annotations
+    testFixturesImplementation libs.slf4j.log4j
 
     integrationTestAnnotationProcessor 
project(":ignite-network-annotation-processor")
 
diff --git 
a/modules/network/src/integrationTest/java/org/apache/ignite/network/scalecube/ItScaleCubeNetworkMessagingTest.java
 
b/modules/network/src/integrationTest/java/org/apache/ignite/network/scalecube/ItScaleCubeNetworkMessagingTest.java
index 782725c95a..9feea21095 100644
--- 
a/modules/network/src/integrationTest/java/org/apache/ignite/network/scalecube/ItScaleCubeNetworkMessagingTest.java
+++ 
b/modules/network/src/integrationTest/java/org/apache/ignite/network/scalecube/ItScaleCubeNetworkMessagingTest.java
@@ -34,6 +34,7 @@ import io.scalecube.cluster.ClusterImpl;
 import io.scalecube.cluster.transport.api.Transport;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 import java.util.Map;
@@ -44,9 +45,7 @@ import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.logging.Handler;
-import java.util.logging.LogRecord;
-import java.util.logging.Logger;
+import java.util.function.Predicate;
 import org.apache.ignite.internal.network.NetworkMessageTypes;
 import org.apache.ignite.internal.network.NetworkMessagesFactory;
 import org.apache.ignite.internal.network.messages.TestMessage;
@@ -56,7 +55,7 @@ import 
org.apache.ignite.internal.network.netty.ConnectionManager;
 import 
org.apache.ignite.internal.network.recovery.RecoveryClientHandshakeManager;
 import 
org.apache.ignite.internal.network.recovery.RecoveryServerHandshakeManager;
 import 
org.apache.ignite.internal.network.recovery.message.HandshakeFinishMessage;
-import org.apache.ignite.internal.testframework.jul.NoOpHandler;
+import org.apache.ignite.internal.testframework.log4j2.LogInspector;
 import org.apache.ignite.lang.IgniteBiTuple;
 import org.apache.ignite.lang.NodeStoppingException;
 import org.apache.ignite.network.ClusterNode;
@@ -68,6 +67,7 @@ import org.apache.ignite.network.NodeFinder;
 import org.apache.ignite.network.StaticNodeFinder;
 import org.apache.ignite.network.TopologyEventHandler;
 import org.apache.ignite.utils.ClusterServiceTestUtils;
+import org.apache.logging.log4j.core.LogEvent;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.TestInfo;
@@ -87,10 +87,15 @@ class ItScaleCubeNetworkMessagingTest {
     /** Message factory. */
     private final TestMessagesFactory messageFactory = new 
TestMessagesFactory();
 
+    /** List of test log inspectors. */
+    private final List<LogInspector> logInspectors = new ArrayList<>();
+
     /** Tear down method. */
     @AfterEach
     public void tearDown() {
         testCluster.shutdown();
+        logInspectors.forEach(LogInspector::stop);
+        logInspectors.clear();
     }
 
     /**
@@ -412,20 +417,19 @@ class ItScaleCubeNetworkMessagingTest {
             }
         });
 
-        Handler rejectedHandshakeHandler = new NoOpHandler() {
-            @Override
-            public void publish(LogRecord record) {
-                if (record.getMessage().startsWith("Handshake rejected by ")) {
-                    ready.countDown();
-                }
-            }
-        };
+        Predicate<LogEvent> matcher = evt -> 
evt.getMessage().getFormattedMessage().startsWith("Handshake rejected by ");
+
+        logInspectors.add(new LogInspector(
+                RecoveryClientHandshakeManager.class.getName(),
+                matcher,
+                ready::countDown));
 
-        Logger clientHandshakeManagerLogger = 
Logger.getLogger(RecoveryClientHandshakeManager.class.getName());
-        clientHandshakeManagerLogger.addHandler(rejectedHandshakeHandler);
+        logInspectors.add(new LogInspector(
+                RecoveryServerHandshakeManager.class.getName(),
+                matcher,
+                ready::countDown));
 
-        Logger serverHandshakeManagerLogger = 
Logger.getLogger(RecoveryServerHandshakeManager.class.getName());
-        serverHandshakeManagerLogger.addHandler(rejectedHandshakeHandler);
+        logInspectors.forEach(LogInspector::start);
 
         testCluster.members.stream()
                 .filter(service -> !outcastName.equals(service.nodeName()))
diff --git a/modules/raft/build.gradle b/modules/raft/build.gradle
index f5479e6a1e..f37d6dd86d 100644
--- a/modules/raft/build.gradle
+++ b/modules/raft/build.gradle
@@ -53,7 +53,6 @@ dependencies {
     testImplementation libs.netty.common
     testImplementation libs.hamcrest.core
     testImplementation libs.jmh.core
-    testImplementation libs.slf4j.jdk14
 
     testFixturesAnnotationProcessor 
project(":ignite-network-annotation-processor")
     testFixturesImplementation project(':ignite-core')
diff --git a/modules/rest/build.gradle b/modules/rest/build.gradle
index 1e07759058..bf5e7e0dc3 100644
--- a/modules/rest/build.gradle
+++ b/modules/rest/build.gradle
@@ -57,7 +57,6 @@ dependencies {
     testImplementation project(':ignite-configuration')
     testImplementation testFixtures(project(':ignite-core'))
     testImplementation testFixtures(project(':ignite-configuration'))
-    testImplementation libs.slf4j.jdk14
     testImplementation libs.micronaut.junit5
     testImplementation libs.micronaut.http.client
     testImplementation libs.mockito.core
diff --git a/modules/runner/build.gradle b/modules/runner/build.gradle
index e793f5cf0b..1d11625efe 100644
--- a/modules/runner/build.gradle
+++ b/modules/runner/build.gradle
@@ -83,7 +83,6 @@ dependencies {
     implementation libs.micronaut.runtime
     implementation libs.micronaut.picocli
     implementation libs.picocli.core
-    implementation libs.slf4j.jdk14
     implementation libs.typesafe.config
     implementation libs.auto.service.annotations
     implementation libs.disruptor
diff --git 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/raftsnapshot/ItTableRaftSnapshotsTest.java
 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/raftsnapshot/ItTableRaftSnapshotsTest.java
index cfc9bc7c9d..c0be52dfa6 100644
--- 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/raftsnapshot/ItTableRaftSnapshotsTest.java
+++ 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/raftsnapshot/ItTableRaftSnapshotsTest.java
@@ -46,9 +46,6 @@ import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Predicate;
 import java.util.function.Supplier;
-import java.util.logging.Handler;
-import java.util.logging.LogRecord;
-import java.util.logging.Logger;
 import java.util.stream.IntStream;
 import org.apache.calcite.sql.validate.SqlValidatorException;
 import org.apache.ignite.internal.Cluster;
@@ -66,7 +63,8 @@ import 
org.apache.ignite.internal.storage.rocksdb.RocksDbStorageEngine;
 import 
org.apache.ignite.internal.table.distributed.raft.snapshot.message.SnapshotMetaResponse;
 import org.apache.ignite.internal.testframework.IgniteTestUtils;
 import org.apache.ignite.internal.testframework.WorkDirectory;
-import org.apache.ignite.internal.testframework.jul.NoOpHandler;
+import org.apache.ignite.internal.testframework.log4j2.LogInspector;
+import org.apache.ignite.internal.testframework.log4j2.LogInspector.Handler;
 import org.apache.ignite.lang.ErrorGroups.Sql;
 import org.apache.ignite.lang.IgniteBiTuple;
 import org.apache.ignite.lang.IgniteException;
@@ -130,7 +128,7 @@ class ItTableRaftSnapshotsTest extends 
IgniteIntegrationTest {
 
     private Cluster cluster;
 
-    private Logger replicatorLogger;
+    private LogInspector replicatorLogInspector;
 
     private @Nullable Handler replicaLoggerHandler;
 
@@ -138,16 +136,18 @@ class ItTableRaftSnapshotsTest extends 
IgniteIntegrationTest {
     void createCluster(TestInfo testInfo) {
         cluster = new Cluster(testInfo, workDir, NODE_BOOTSTRAP_CFG);
 
-        replicatorLogger = Logger.getLogger(Replicator.class.getName());
+        replicatorLogInspector = LogInspector.create(Replicator.class, true);
     }
 
     @AfterEach
     @Timeout(60)
     void shutdownCluster() {
         if (replicaLoggerHandler != null) {
-            replicatorLogger.removeHandler(replicaLoggerHandler);
+            replicatorLogInspector.removeHandler(replicaLoggerHandler);
         }
 
+        replicatorLogInspector.stop();
+
         cluster.shutdown();
     }
 
@@ -464,23 +464,18 @@ class ItTableRaftSnapshotsTest extends 
IgniteIntegrationTest {
     private void reanimateNodeAndWaitForSnapshotInstalled(int nodeIndex) 
throws InterruptedException {
         CountDownLatch snapshotInstalledLatch = new CountDownLatch(1);
 
-        var handler = new NoOpHandler() {
-            @Override
-            public void publish(LogRecord record) {
-                if (record.getMessage().matches("Node .+ received 
InstallSnapshotResponse from .+_" + nodeIndex + " .+ success=true")) {
-                    snapshotInstalledLatch.countDown();
-                }
-            }
-        };
-
-        replicatorLogger.addHandler(handler);
+        Handler handler = replicatorLogInspector.addHandler(
+                evt -> evt.getMessage().getFormattedMessage().matches(
+                        "Node .+ received InstallSnapshotResponse from .+_" + 
nodeIndex + " .+ success=true"),
+                () -> snapshotInstalledLatch.countDown()
+        );
 
         try {
             reanimateNode(nodeIndex);
 
             assertTrue(snapshotInstalledLatch.await(60, TimeUnit.SECONDS), 
"Did not install a snapshot in time");
         } finally {
-            replicatorLogger.removeHandler(handler);
+            replicatorLogInspector.removeHandler(handler);
         }
     }
 
@@ -734,18 +729,15 @@ class ItTableRaftSnapshotsTest extends 
IgniteIntegrationTest {
     void 
snapshotInstallTimeoutDoesNotBreakSubsequentInstallsWhenSecondAttemptIsIdenticalToFirst()
 throws Exception {
         AtomicBoolean snapshotInstallFailedDueToIdenticalRetry = new 
AtomicBoolean(false);
 
-        Logger snapshotExecutorLogger = 
Logger.getLogger(SnapshotExecutorImpl.class.getName());
+        LogInspector snapshotExecutorLogInspector = 
LogInspector.create(SnapshotExecutorImpl.class);
 
-        var snapshotInstallFailedDueToIdenticalRetryHandler = new 
NoOpHandler() {
-            @Override
-            public void publish(LogRecord record) {
-                if (record.getMessage().contains("Register DownloadingSnapshot 
failed: interrupted by retry installing request")) {
-                    snapshotInstallFailedDueToIdenticalRetry.set(true);
-                }
-            }
-        };
+        Handler snapshotInstallFailedDueToIdenticalRetryHandler =
+                snapshotExecutorLogInspector.addHandler(
+                        evt -> evt.getMessage().getFormattedMessage().contains(
+                                "Register DownloadingSnapshot failed: 
interrupted by retry installing request"),
+                        () -> 
snapshotInstallFailedDueToIdenticalRetry.set(true));
 
-        
snapshotExecutorLogger.addHandler(snapshotInstallFailedDueToIdenticalRetryHandler);
+        snapshotExecutorLogInspector.start();
 
         try {
             prepareClusterForInstallingSnapshotToNode2(DEFAULT_STORAGE_ENGINE, 
theCluster -> {
@@ -764,7 +756,8 @@ class ItTableRaftSnapshotsTest extends 
IgniteIntegrationTest {
 
             reanimateNode2AndWaitForSnapshotInstalled();
         } finally {
-            
snapshotExecutorLogger.removeHandler(snapshotInstallFailedDueToIdenticalRetryHandler);
+            
snapshotExecutorLogInspector.removeHandler(snapshotInstallFailedDueToIdenticalRetryHandler);
+            snapshotExecutorLogInspector.stop();
         }
     }
 
@@ -805,7 +798,7 @@ class ItTableRaftSnapshotsTest extends 
IgniteIntegrationTest {
     }
 
     /**
-     * Adds a listener for the {@link #replicatorLogger} to hear the success 
of the snapshot installation.
+     * Adds a listener for the {@link #replicatorLogInspector} to hear the 
success of the snapshot installation.
      */
     private void listenForSnapshotInstalledSuccessFromLogger(
             int nodeIndexFrom,
@@ -814,19 +807,9 @@ class ItTableRaftSnapshotsTest extends 
IgniteIntegrationTest {
     ) {
         String regexp = "Node .+" + nodeIndexFrom + " received 
InstallSnapshotResponse from .+_" + nodeIndexTo + " .+ success=true";
 
-        replicaLoggerHandler = new NoOpHandler() {
-            @Override
-            public void publish(LogRecord record) {
-                if (record.getMessage().matches(regexp)) {
-                    snapshotInstallSuccessfullyFuture.complete(null);
-
-                    replicatorLogger.removeHandler(this);
-                    replicaLoggerHandler = null;
-                }
-            }
-        };
-
-        replicatorLogger.addHandler(replicaLoggerHandler);
+        replicaLoggerHandler = replicatorLogInspector.addHandler(
+                evt -> evt.getMessage().getFormattedMessage().matches(regexp),
+                () -> snapshotInstallSuccessfullyFuture.complete(null));
     }
 
     /**
diff --git 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/start/ItStartTest.java
 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/start/ItStartTest.java
index f1fbef40e7..52f363a48b 100644
--- 
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/start/ItStartTest.java
+++ 
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/start/ItStartTest.java
@@ -26,19 +26,19 @@ import static org.hamcrest.Matchers.startsWith;
 import static org.junit.jupiter.api.Assertions.assertAll;
 
 import java.nio.file.Path;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.atomic.AtomicReference;
-import java.util.logging.Handler;
-import java.util.logging.LogRecord;
-import java.util.logging.Logger;
 import org.apache.ignite.internal.Cluster;
 import org.apache.ignite.internal.IgniteIntegrationTest;
 import org.apache.ignite.internal.app.IgniteImpl;
 import org.apache.ignite.internal.index.IndexManager;
 import org.apache.ignite.internal.testframework.IgniteTestUtils;
 import org.apache.ignite.internal.testframework.WorkDirectory;
-import org.apache.ignite.internal.testframework.jul.NoOpHandler;
+import org.apache.ignite.internal.testframework.log4j2.LogInspector;
+import org.apache.ignite.internal.testframework.log4j2.LogInspector.Handler;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -79,14 +79,17 @@ class ItStartTest extends IgniteIntegrationTest {
                 new Expectation("startComplete", 
"org.apache.ignite.internal.app.LifecycleManager", "Start complete")
         );
 
+        Map<String, LogInspector> inspectors = new HashMap<>();
+
         List<LoggingProbe> probes = expectations.stream()
-                .map(ItStartTest::installProbe)
+                .map(expectation -> installProbe(expectation, inspectors))
                 .collect(toList());
 
         try {
             cluster.startAndInit(1);
         } finally {
             probes.forEach(LoggingProbe::cleanup);
+            inspectors.values().forEach(LogInspector::stop);
         }
 
         assertAll(
@@ -104,23 +107,18 @@ class ItStartTest extends IgniteIntegrationTest {
         return "%" + IgniteTestUtils.testNodeName(testInfo, 0) + "%start-";
     }
 
-    private static LoggingProbe installProbe(Expectation expectation) {
-        Logger logger = Logger.getLogger(expectation.loggerClassName);
+    private static LoggingProbe installProbe(Expectation expectation, 
Map<String, LogInspector> inspectors) {
+        LogInspector inspector = inspectors.computeIfAbsent(
+                expectation.loggerClassName,
+                loggerClassName -> LogInspector.create(loggerClassName, true));
 
         AtomicReference<String> threadNameRef = new AtomicReference<>();
 
-        var handler = new NoOpHandler() {
-            @Override
-            public void publish(LogRecord record) {
-                if (record.getMessage().matches(expectation.messageRegexp)) {
-                    threadNameRef.set(Thread.currentThread().getName());
-                }
-            }
-        };
-
-        logger.addHandler(handler);
+        Handler handler = inspector.addHandler(
+                evt -> 
evt.getMessage().getFormattedMessage().matches(expectation.messageRegexp),
+                () -> threadNameRef.set(Thread.currentThread().getName()));
 
-        return new LoggingProbe(expectation, logger, handler, threadNameRef);
+        return new LoggingProbe(expectation, inspector, handler, 
threadNameRef);
     }
 
     @Test
@@ -170,19 +168,19 @@ class ItStartTest extends IgniteIntegrationTest {
 
     private static class LoggingProbe {
         private final Expectation expectation;
-        private final Logger logger;
+        private final LogInspector inspector;
         private final Handler handler;
         private final AtomicReference<String> threadNameRef;
 
-        private LoggingProbe(Expectation expectation, Logger logger, Handler 
handler, AtomicReference<String> threadNameRef) {
+        private LoggingProbe(Expectation expectation, LogInspector inspector, 
Handler handler, AtomicReference<String> threadNameRef) {
             this.expectation = expectation;
-            this.logger = logger;
+            this.inspector = inspector;
             this.handler = handler;
             this.threadNameRef = threadNameRef;
         }
 
         void cleanup() {
-            logger.removeHandler(handler);
+            inspector.removeHandler(handler);
         }
     }
 }
diff --git a/modules/sql-engine/build.gradle b/modules/sql-engine/build.gradle
index 20da54336e..c7e33c78cf 100644
--- a/modules/sql-engine/build.gradle
+++ b/modules/sql-engine/build.gradle
@@ -86,7 +86,6 @@ dependencies {
     testImplementation libs.mockito.junit
     testImplementation libs.mockito.core
     testImplementation libs.hamcrest.core
-    testImplementation libs.slf4j.jdk14
     testImplementation libs.archunit.core
     testImplementation libs.archunit.junit5
 
diff --git a/modules/table/build.gradle b/modules/table/build.gradle
index f9f41e1a48..c57b42a7fd 100644
--- a/modules/table/build.gradle
+++ b/modules/table/build.gradle
@@ -68,7 +68,6 @@ dependencies {
     testImplementation libs.hamcrest.core
     testImplementation libs.hamcrest.optional
     testImplementation libs.junit.pioneer
-    testImplementation libs.slf4j.jdk14
     testImplementation libs.jmh.core
     testImplementation libs.javax.annotations
 

Reply via email to