Skip to content

Commit

Permalink
+ feature: junit5 test plan listener to auto stop elf4j engine at the…
Browse files Browse the repository at this point in the history
… end of test plan execution.
  • Loading branch information
q3769 committed Aug 5, 2023
1 parent 592d9da commit 3b3e8d6
Show file tree
Hide file tree
Showing 13 changed files with 115 additions and 71 deletions.
10 changes: 8 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@

<groupId>io.github.elf4j</groupId>
<artifactId>elf4j-engine</artifactId>
<version>11.0.9</version>
<version>11.1.0</version>
<packaging>jar</packaging>
<name>elf4j-engine</name>
<description>A stand-alone Java log engine implementing the ELF4J (Easy Logging Facade for Java) API</description>
Expand Down Expand Up @@ -77,7 +77,7 @@
<dependency>
<groupId>io.github.q3769</groupId>
<artifactId>conseq4j</artifactId>
<version>20230619.0.0</version>
<version>20230619.20230729.20230730</version>
</dependency>
<dependency>
<groupId>com.dslplatform</groupId>
Expand Down Expand Up @@ -119,6 +119,12 @@
<version>5.4.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<version>1.9.3</version>
<scope>compile</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,17 @@
import lombok.NonNull;

import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;

/**
*
*/
public class BufferedLogEventProcessor implements LogEventProcessor {
private static final int DEFAULT_FRONT_BUFFER_CAPACITY = Integer.MAX_VALUE;
private final LogWriter logWriter;
private final ExecutorService bufferedExecutor;
private final ThreadPoolExecutor bufferedExecutor;

private BufferedLogEventProcessor(LogWriter logWriter, ExecutorService bufferedExecutor) {
private BufferedLogEventProcessor(LogWriter logWriter, ThreadPoolExecutor bufferedExecutor) {
this.logWriter = logWriter;
this.bufferedExecutor = bufferedExecutor;
LogServiceManager.INSTANCE.registerStop(this);
Expand Down Expand Up @@ -77,12 +77,17 @@ public void process(LogEvent logEvent) {

@Override
public void stop() {
IeLogger.INFO.log("Stopping: {}", this);
IeLogger.INFO.log("Stopping {}", this);
this.bufferedExecutor.shutdown();
}

@Override
public boolean isStopped() {
return this.bufferedExecutor.isTerminated();
}

@Override
public boolean isIdle() {
return bufferedExecutor.getActiveCount() == 0;
}
}
2 changes: 1 addition & 1 deletion src/main/java/elf4j/engine/service/LogEventProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
/**
*
*/
public interface LogEventProcessor extends Stoppable.Intake {
public interface LogEventProcessor extends Stoppable {
/**
* @param logEvent
* to process
Expand Down
33 changes: 12 additions & 21 deletions src/main/java/elf4j/engine/service/LogServiceManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@
import org.awaitility.Awaitility;
import org.awaitility.core.ConditionFactory;

import java.util.*;
import java.util.stream.Collectors;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;

/**
*
Expand All @@ -43,17 +44,10 @@ public enum LogServiceManager {
*/
INSTANCE;

private static final List<Class<? extends Stoppable>> ORDERED_STOPPABLE_TYPES =
Arrays.asList(Stoppable.Intake.class, Stoppable.Process.class, Stoppable.Output.class);

private final Set<Refreshable> refreshables = new HashSet<>();
private final Set<Stoppable> stoppables = new HashSet<>();
private final ConditionFactory await = Awaitility.await().forever();

private static boolean allStopped(@NonNull Collection<Stoppable> stoppables) {
return stoppables.stream().allMatch(Stoppable::isStopped);
}

/**
* @param refreshable
* added to be accessible for management
Expand Down Expand Up @@ -93,24 +87,21 @@ public void refresh(Properties properties) {
/**
*
*/
public void stop() {
IeLogger.INFO.log("Stopping elf4j service...");
ORDERED_STOPPABLE_TYPES.forEach(this::awaitStop);
IeLogger.INFO.log("Stopped elf4j service");
public void shutdown() {
IeLogger.INFO.log("Start shutting down elf4j service...");
IeLogger.INFO.log("Awaiting all log processors to complete...");
await.until(() -> stoppables.stream().allMatch(Stoppable::isIdle));
IeLogger.INFO.log("All log processors completed");
stoppables.forEach(Stoppable::stop);
IeLogger.INFO.log("End shutting down elf4j service");
}

/**
* @return a thread that orderly stops the entire log service. As an alternative to calling the {@link #stop()}, the
* @return a thread that orderly stops the entire log service. As an alternative to calling {@link #shutdown()}, the
* returned thread can be registered as a JVM shutdown hook.
*/
@NonNull
public Thread getShutdownHookThread() {
return new Thread(this::stop);
}

private void awaitStop(Class<? extends Stoppable> targetType) {
List<Stoppable> stopTargets = stoppables.stream().filter(targetType::isInstance).collect(Collectors.toList());
stopTargets.stream().parallel().forEach(Stoppable::stop);
this.await.until(() -> allStopped(stopTargets));
return new Thread(this::shutdown);
}
}
18 changes: 2 additions & 16 deletions src/main/java/elf4j/engine/service/Stoppable.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,21 +40,7 @@ public interface Stoppable {
boolean isStopped();

/**
*
*/
interface Intake extends Stoppable {
}

/**
*
*/
interface Output extends Stoppable {

}

/**
*
* @return true if, by estimation, no worker/thread is actively executing tasks.
*/
interface Process extends Stoppable {
}
boolean isIdle();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* MIT License
*
* Copyright (c) 2023 Qingtian Wang
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/

package elf4j.engine.service.util;

import elf4j.engine.service.LogServiceManager;
import org.junit.platform.launcher.TestExecutionListener;
import org.junit.platform.launcher.TestPlan;

/**
*
*/
public class Elf4jPostTestProcessor implements TestExecutionListener {

@Override
public void testPlanExecutionFinished(TestPlan testPlan) {
LogServiceManager.INSTANCE.shutdown();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
* the orginal thread.
*/
@ToString
public class ConseqWriterGroup implements LogWriter, Stoppable.Process {
public class ConseqWriterGroup implements LogWriter, Stoppable {
private static final int DEFAULT_CONSEQ_CONCURRENCY = Runtime.getRuntime().availableProcessors();
private final List<LogWriter> writers;
private final ConseqExecutor conseqExecutor;
Expand Down Expand Up @@ -146,12 +146,17 @@ public boolean includeCallerThread() {

@Override
public void stop() {
IeLogger.INFO.log("Stopping: {}", this);
IeLogger.INFO.log("Stopping {}", this);
conseqExecutor.shutdown();
}

@Override
public boolean isStopped() {
return conseqExecutor.isTerminated();
}

@Override
public boolean isIdle() {
return this.conseqExecutor.isIdle();
}
}
9 changes: 0 additions & 9 deletions src/test/java/elf4j/engine/IntegrationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,13 @@

package elf4j.engine;

import coco4j.MoreAwaitilities;
import elf4j.Logger;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

import java.time.Duration;

import static org.junit.jupiter.api.Assertions.assertEquals;

class IntegrationTest {
@AfterEach
void afterEach() {
MoreAwaitilities.sleepInterruptibly(Duration.ofMillis(500));
}

@Nested
class defaultLogger {
@Test
Expand Down
6 changes: 1 addition & 5 deletions src/test/java/elf4j/engine/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,9 @@

package elf4j.engine;

import coco4j.MoreAwaitilities;
import elf4j.Logger;
import elf4j.engine.service.LogServiceManager;

import java.time.Duration;
import java.util.function.Supplier;

public class Main {
Expand Down Expand Up @@ -57,8 +55,6 @@ public static void main(String[] args) {
.atTrace()
.atDebug()
.log("Not a practical example but now the severity level is DEBUG");

MoreAwaitilities.sleepInterruptibly(Duration.ofMillis(200));
LogServiceManager.INSTANCE.stop();
LogServiceManager.INSTANCE.shutdown();
}
}
8 changes: 0 additions & 8 deletions src/test/java/elf4j/engine/SampleUsageTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,15 @@

package elf4j.engine;

import coco4j.MoreAwaitilities;
import elf4j.Logger;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

import java.time.Duration;
import java.util.function.Supplier;

class SampleUsageTest {
static Logger logger = Logger.instance();

@AfterEach
void afterEach() {
MoreAwaitilities.sleepInterruptibly(Duration.ofMillis(500));
}

@Nested
class plainText {
@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ public void stop() {
public boolean isStopped() {
return false;
}

@Override
public boolean isIdle() {
return false;
}
}

@Nested
Expand Down Expand Up @@ -177,4 +182,4 @@ void onlyLogWhenEnabled() {
then(mockLogServiceConfiguration).should(never()).getLogServiceWriter();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#
# MIT License
#
# Copyright (c) 2023 Qingtian Wang
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
#

elf4j.engine.service.util.Elf4jPostTestProcessor
4 changes: 2 additions & 2 deletions src/test/resources/elf4j-test.properties
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ level@org.springframework=warn
level@org.apache=error
### Any writer is optional, default to a single standard writer if no writer configured
### Default writer out stream type when omitted at writer level - stdout/stderr/auto, default to stdout. 'auto' means to use stdout if severity level is lower than WARN, otherwise use stderr
stream=stderr
#stream=stderr
### Overriding default writer pattern (optional)
pattern={timestamp:yyyy-MM-dd HH:mm:ss} {level} {thread} {class:full} - {message}
writer1=standard
Expand All @@ -54,4 +54,4 @@ writer4=standard
writer4.pattern={timestamp:yyyy-MM-dd'T'HH:mm:ss.SSSXXX} {level:5} [{thread:name}] {class:compressed}#{method}(L{linenumber}@{filename}) -- {message}
### Optional buffer - log event processor work queue capacity, default "unlimited" log events (as hydrated in-memory objects)
#buffer=2048
#concurrency=20
#concurrency=20

0 comments on commit 3b3e8d6

Please sign in to comment.