-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add ScopedContext and ResourceLogger * ResourceLogger uses ScopedContext * Use ExtendedLoggerWrapper * Move ContextDataProviders to the API * Update docs * Remove ParameterizedMapMessage * Remove ParameterizedMapMessage from test * Fix typo * Ensure unit test uses a unique file name * Move non-API classes to core * Revert unit test change. Correct site changes * Fix comment * Remove the Scopedcontext.Renderable interface * Fix site conflicts * Delegate `ScopedContext` functionality to interface To provide more configurability for the `ScopedContext` service, this PR moves its implementation details to `log4j-core` and replaces it with a `ScopedContextProvider` interface. In Log4j API only a NO-OP version of the provider is present, but each implementation of the API can provide its own. * Incorporate Piotr's changes - mostly * Return raw object as it was stored. Add wrap methods * More abstractions * Fix javadoc comment --------- Co-authored-by: Piotr P. Karwasz <piotr.github@karwasz.org>
- Loading branch information
Showing
52 changed files
with
2,902 additions
and
212 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
178 changes: 178 additions & 0 deletions
178
...-api-test/src/main/java/org/apache/logging/log4j/test/spi/ScopedContextProviderSuite.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
/* | ||
* 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.logging.log4j.test.spi; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.assertj.core.api.Assertions.assertThatThrownBy; | ||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; | ||
import static org.junit.jupiter.api.Assertions.assertNotEquals; | ||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
||
import java.util.concurrent.ArrayBlockingQueue; | ||
import java.util.concurrent.BlockingQueue; | ||
import java.util.concurrent.Callable; | ||
import java.util.concurrent.ExecutorService; | ||
import java.util.concurrent.Future; | ||
import java.util.concurrent.ThreadPoolExecutor; | ||
import java.util.concurrent.TimeUnit; | ||
import java.util.concurrent.atomic.AtomicBoolean; | ||
import java.util.concurrent.atomic.AtomicInteger; | ||
import java.util.concurrent.atomic.AtomicLong; | ||
import org.apache.logging.log4j.ScopedContext; | ||
import org.apache.logging.log4j.spi.ScopedContextProvider; | ||
|
||
/** | ||
* Provides test that should be passed by all implementations of {@link ScopedContextProviderSuite}. | ||
* @since 2.24.0 | ||
*/ | ||
public abstract class ScopedContextProviderSuite { | ||
|
||
private static ScopedContext.Instance where( | ||
final ScopedContextProvider provider, final String key, final Object value) { | ||
return provider.newScopedContext(key, value); | ||
} | ||
|
||
protected static void testScope(final ScopedContextProvider scopedContext) { | ||
where(scopedContext, "key1", "Log4j2") | ||
.run(() -> assertThat(scopedContext.getValue("key1")).isEqualTo("Log4j2")); | ||
where(scopedContext, "key1", "value1").run(() -> { | ||
assertThat(scopedContext.getValue("key1")).isEqualTo("value1"); | ||
where(scopedContext, "key2", "value2").run(() -> { | ||
assertThat(scopedContext.getValue("key1")).isEqualTo("value1"); | ||
assertThat(scopedContext.getValue("key2")).isEqualTo("value2"); | ||
}); | ||
}); | ||
} | ||
|
||
private static void runWhere( | ||
final ScopedContextProvider provider, final String key, final Object value, final Runnable task) { | ||
provider.newScopedContext(key, value).run(task); | ||
} | ||
|
||
private static Future<Void> runWhere( | ||
final ScopedContextProvider provider, | ||
final String key, | ||
final Object value, | ||
final ExecutorService executorService, | ||
final Runnable task) { | ||
return provider.newScopedContext(key, value).run(executorService, task); | ||
} | ||
|
||
protected static void testRunWhere(final ScopedContextProvider scopedContext) { | ||
runWhere(scopedContext, "key1", "Log4j2", () -> assertThat(scopedContext.getValue("key1")) | ||
.isEqualTo("Log4j2")); | ||
runWhere(scopedContext, "key1", "value1", () -> { | ||
assertThat(scopedContext.getValue("key1")).isEqualTo("value1"); | ||
runWhere(scopedContext, "key2", "value2", () -> { | ||
assertThat(scopedContext.getValue("key1")).isEqualTo("value1"); | ||
assertThat(scopedContext.getValue("key2")).isEqualTo("value2"); | ||
}); | ||
}); | ||
} | ||
|
||
protected static void testRunThreads(final ScopedContextProvider scopedContext) { | ||
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(5); | ||
ExecutorService executorService = new ThreadPoolExecutor(1, 2, 30, TimeUnit.SECONDS, workQueue); | ||
final long id = Thread.currentThread().getId(); | ||
final AtomicLong counter = new AtomicLong(0); | ||
runWhere(scopedContext, "key1", "Log4j2", () -> { | ||
assertThat(scopedContext.getValue("key1")).isEqualTo("Log4j2"); | ||
Future<?> future = runWhere(scopedContext, "key2", "value2", executorService, () -> { | ||
assertNotEquals(Thread.currentThread().getId(), id); | ||
assertThat(scopedContext.getValue("key1")).isEqualTo("Log4j2"); | ||
counter.incrementAndGet(); | ||
}); | ||
assertDoesNotThrow(() -> { | ||
future.get(); | ||
assertTrue(future.isDone()); | ||
assertThat(counter.get()).isEqualTo(1); | ||
}); | ||
}); | ||
} | ||
|
||
protected static void testThreads(final ScopedContextProvider scopedContext) throws Exception { | ||
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(5); | ||
ExecutorService executorService = new ThreadPoolExecutor(1, 2, 30, TimeUnit.SECONDS, workQueue); | ||
final long id = Thread.currentThread().getId(); | ||
final AtomicLong counter = new AtomicLong(0); | ||
where(scopedContext, "key1", "Log4j2").run(() -> { | ||
assertThat(scopedContext.getValue("key1")).isEqualTo("Log4j2"); | ||
Future<?> future = where(scopedContext, "key2", "value2").run(executorService, () -> { | ||
assertNotEquals(Thread.currentThread().getId(), id); | ||
assertThat(scopedContext.getValue("key1")).isEqualTo("Log4j2"); | ||
counter.incrementAndGet(); | ||
}); | ||
assertDoesNotThrow(() -> { | ||
future.get(); | ||
assertTrue(future.isDone()); | ||
assertThat(counter.get()).isEqualTo(1); | ||
}); | ||
}); | ||
} | ||
|
||
protected static void testThreadException(final ScopedContextProvider scopedContext) throws Exception { | ||
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(5); | ||
final AtomicBoolean exceptionCaught = new AtomicBoolean(false); | ||
ExecutorService executorService = new ThreadPoolExecutor(1, 2, 30, TimeUnit.SECONDS, workQueue); | ||
long id = Thread.currentThread().getId(); | ||
runWhere(scopedContext, "key1", "Log4j2", () -> { | ||
assertThat(scopedContext.getValue("key1")).isEqualTo("Log4j2"); | ||
Future<?> future = where(scopedContext, "key2", "value2").run(executorService, () -> { | ||
assertNotEquals(Thread.currentThread().getId(), id); | ||
throw new NullPointerException("On purpose NPE"); | ||
}); | ||
assertThatThrownBy(future::get) | ||
.hasRootCauseInstanceOf(NullPointerException.class) | ||
.hasRootCauseMessage("On purpose NPE"); | ||
}); | ||
} | ||
|
||
private static <R> R callWhere( | ||
final ScopedContextProvider provider, final String key, final Object value, final Callable<R> task) | ||
throws Exception { | ||
return provider.newScopedContext(key, value).call(task); | ||
} | ||
|
||
private static <R> Future<R> callWhere( | ||
final ScopedContextProvider provider, | ||
final String key, | ||
final Object value, | ||
final ExecutorService executorService, | ||
final Callable<R> task) { | ||
return provider.newScopedContext(key, value).call(executorService, task); | ||
} | ||
|
||
protected static void testThreadCall(final ScopedContextProvider scopedContext) throws Exception { | ||
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(5); | ||
ExecutorService executorService = new ThreadPoolExecutor(1, 2, 30, TimeUnit.SECONDS, workQueue); | ||
final long id = Thread.currentThread().getId(); | ||
final AtomicInteger counter = new AtomicInteger(0); | ||
int returnVal = callWhere(scopedContext, "key1", "Log4j2", () -> { | ||
assertThat(scopedContext.getValue("key1")).isEqualTo("Log4j2"); | ||
Future<Integer> future = callWhere(scopedContext, "key2", "value2", executorService, () -> { | ||
assertNotEquals(Thread.currentThread().getId(), id); | ||
assertThat(scopedContext.getValue("key1")).isEqualTo("Log4j2"); | ||
return counter.incrementAndGet(); | ||
}); | ||
Integer val = future.get(); | ||
assertTrue(future.isDone()); | ||
assertThat(counter.get()).isEqualTo(1); | ||
return val; | ||
}); | ||
assertThat(returnVal).isEqualTo(1); | ||
} | ||
} |
141 changes: 141 additions & 0 deletions
141
log4j-api-test/src/test/java/org/apache/logging/log4j/ResourceLoggerTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
/* | ||
* 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.logging.log4j; | ||
|
||
import static org.hamcrest.MatcherAssert.assertThat; | ||
import static org.hamcrest.Matchers.containsString; | ||
import static org.hamcrest.Matchers.hasSize; | ||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.concurrent.atomic.AtomicLong; | ||
import java.util.function.Supplier; | ||
import org.apache.logging.log4j.test.TestLogger; | ||
import org.apache.logging.log4j.test.TestLoggerContextFactory; | ||
import org.junit.jupiter.api.BeforeAll; | ||
import org.junit.jupiter.api.Test; | ||
|
||
/** | ||
* Class Description goes here. | ||
*/ | ||
public class ResourceLoggerTest { | ||
@BeforeAll | ||
public static void beforeAll() { | ||
System.setProperty("log4j2.loggerContextFactory", TestLoggerContextFactory.class.getName()); | ||
} | ||
|
||
@BeforeAll | ||
public static void afterAll() { | ||
System.clearProperty("log4j2.loggerContextFactory"); | ||
} | ||
|
||
@Test | ||
public void testFactory() throws Exception { | ||
Connection connection = new Connection("Test", "dummy"); | ||
connection.useConnection(); | ||
MapSupplier mapSupplier = new MapSupplier(connection); | ||
Logger logger = ResourceLogger.newBuilder() | ||
.withClass(this.getClass()) | ||
.withSupplier(mapSupplier) | ||
.build(); | ||
logger.debug("Hello, {}", "World"); | ||
Logger log = LogManager.getLogger(this.getClass().getName()); | ||
assertTrue(log instanceof TestLogger); | ||
TestLogger testLogger = (TestLogger) log; | ||
List<String> events = testLogger.getEntries(); | ||
assertThat(events, hasSize(1)); | ||
assertThat(events.get(0), containsString("Name=Test")); | ||
assertThat(events.get(0), containsString("Type=dummy")); | ||
assertThat(events.get(0), containsString("Count=1")); | ||
assertThat(events.get(0), containsString("Hello, World")); | ||
events.clear(); | ||
connection.useConnection(); | ||
logger.debug("Used the connection"); | ||
assertThat(events.get(0), containsString("Count=2")); | ||
assertThat(events.get(0), containsString("Used the connection")); | ||
events.clear(); | ||
connection = new Connection("NewConnection", "fiber"); | ||
connection.useConnection(); | ||
mapSupplier = new MapSupplier(connection); | ||
logger = ResourceLogger.newBuilder().withSupplier(mapSupplier).build(); | ||
logger.debug("Connection: {}", "NewConnection"); | ||
assertThat(events, hasSize(1)); | ||
assertThat(events.get(0), containsString("Name=NewConnection")); | ||
assertThat(events.get(0), containsString("Type=fiber")); | ||
assertThat(events.get(0), containsString("Count=1")); | ||
assertThat(events.get(0), containsString("Connection: NewConnection")); | ||
events.clear(); | ||
} | ||
|
||
private static class MapSupplier implements Supplier<Map<String, ?>> { | ||
|
||
private final Connection connection; | ||
|
||
public MapSupplier(final Connection connection) { | ||
this.connection = connection; | ||
} | ||
|
||
@Override | ||
public Map<String, ?> get() { | ||
Map<String, String> map = new HashMap<>(); | ||
map.put("Name", connection.name); | ||
map.put("Type", connection.type); | ||
map.put("Count", Long.toString(connection.getCounter())); | ||
return map; | ||
} | ||
|
||
@Override | ||
public boolean equals(Object o) { | ||
return o instanceof MapSupplier; | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
return 77; | ||
} | ||
} | ||
|
||
private static class Connection { | ||
|
||
private final String name; | ||
private final String type; | ||
private final AtomicLong counter = new AtomicLong(0); | ||
|
||
public Connection(final String name, final String type) { | ||
this.name = name; | ||
this.type = type; | ||
} | ||
|
||
public String getName() { | ||
return name; | ||
} | ||
|
||
public String getType() { | ||
return type; | ||
} | ||
|
||
public long getCounter() { | ||
return counter.get(); | ||
} | ||
|
||
public void useConnection() { | ||
counter.incrementAndGet(); | ||
} | ||
} | ||
} |
Oops, something went wrong.