Add SLF4J Compatibility subsystem

This commit is contained in:
JeremyStar™ 2024-06-11 20:01:34 +02:00
parent f9d5a4e124
commit 0a6ed79673
Signed by: JeremyStarTM
GPG key ID: E366BAEF67E4704D
18 changed files with 570 additions and 10 deletions

View file

@ -135,6 +135,7 @@ public final class Engine implements SubsystemMainClass {
crashContentEngineDependencies.put("Jetbrains Annotations", "%engine_dependency_jetbrains_annotations%"); crashContentEngineDependencies.put("Jetbrains Annotations", "%engine_dependency_jetbrains_annotations%");
crashContentEngineDependencies.put("Jansi", "%engine_dependency_jansi%"); crashContentEngineDependencies.put("Jansi", "%engine_dependency_jansi%");
crashContentEngineDependencies.put("Reflections", "%engine_dependencies_reflections%"); crashContentEngineDependencies.put("Reflections", "%engine_dependencies_reflections%");
crashContentEngineDependencies.put("SLF4J", "%engine_dependencies_slf4j%");
// Engine -> Plugins // Engine -> Plugins
Map<String, String> crashContentEnginePlugins = new LinkedHashMap<>(); Map<String, String> crashContentEnginePlugins = new LinkedHashMap<>();
crashContentEnginePlugins.put("Shadow", "%engine_plugin_shadow%"); crashContentEnginePlugins.put("Shadow", "%engine_plugin_shadow%");

View file

@ -158,6 +158,17 @@ public final class EngineInformation {
private String dependencyReflections; private String dependencyReflections;
/** /**
* Provides the version of the dependency {@code SLF4J}.
*
* @since 1-alpha0
*
* -- GETTER --
* Provides the version of the dependency {@code SLF4J}.
*
* @return SLF4J dependency version
* @since 1-alpha0
*/
private String dependencySlf4j;
/** /**
* Provides the version of the Gradle plugin {@code Shadow}. * Provides the version of the Gradle plugin {@code Shadow}.
@ -231,6 +242,7 @@ public final class EngineInformation {
dependencyJetbrainsAnnotations = parser.getString("dependencyJetbrainsAnnotations"); dependencyJetbrainsAnnotations = parser.getString("dependencyJetbrainsAnnotations");
dependencyJansi = parser.getString("dependencyJansi"); dependencyJansi = parser.getString("dependencyJansi");
dependencyReflections = parser.getString("dependencyReflections"); dependencyReflections = parser.getString("dependencyReflections");
dependencySlf4j = parser.getString("dependencySlf4j");
pluginShadow = parser.getString("pluginShadow"); pluginShadow = parser.getString("pluginShadow");
pluginLombok = parser.getString("pluginLombok"); pluginLombok = parser.getString("pluginLombok");

View file

@ -0,0 +1,53 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Contributors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.sosengine.base.data.placeholders;
import de.staropensource.sosengine.base.classes.Placeholder;
import de.staropensource.sosengine.base.data.info.EngineInformation;
import org.jetbrains.annotations.NotNull;
/**
* Implements the {@code engine_dependency_slf4j} placeholder.
*
* @see Placeholder
* @since 1-alpha0
*/
@SuppressWarnings({ "unused" })
public final class EngineDependencySlf4j implements Placeholder {
/**
* Constructor.
*
* @since 1-alpha0
*/
public EngineDependencySlf4j() {}
/** {@inheritDoc} */
@NotNull
public String getName() {
return "engine_dependency_slf4j";
}
/** {@inheritDoc} */
@NotNull
@Override
public String replace(@NotNull String text) {
return text.replace("%" + getName() + "%", EngineInformation.getInstance().getDependencySlf4j());
}
}

View file

@ -120,7 +120,8 @@ public final class Logger {
base = loggerImplementation.postPlaceholder(level, logIssuer, base); base = loggerImplementation.postPlaceholder(level, logIssuer, base);
// Call event // Call event
new LogEvent().callEvent(level, logIssuer, message); if (!logIssuer.getClazz().getName().equals("de.staropensource.sosengine.slf4j_compat.CompatibilityLogger"))
new LogEvent().callEvent(level, logIssuer, message);
// Print log message // Print log message
loggerImplementation.print(level, logIssuer, base); loggerImplementation.print(level, logIssuer, base);

View file

@ -92,6 +92,7 @@ public final class PlaceholderEngine {
placeholders.add(new EngineDependencyJetbrainsAnnotations()); placeholders.add(new EngineDependencyJetbrainsAnnotations());
placeholders.add(new EngineDependencyJansi()); placeholders.add(new EngineDependencyJansi());
placeholders.add(new EngineDependencyReflections()); placeholders.add(new EngineDependencyReflections());
placeholders.add(new EngineDependencySlf4j());
// engine_plugin_* // engine_plugin_*
placeholders.add(new EnginePluginLombok()); placeholders.add(new EnginePluginLombok());
placeholders.add(new EnginePluginShadow()); placeholders.add(new EnginePluginShadow());

View file

@ -11,6 +11,7 @@ dependencyJetbrainsAnnotations=24.1.0
# Subsystem dependencies # Subsystem dependencies
dependencyJansi=2.4.1 dependencyJansi=2.4.1
dependencyReflections=0.10.2 dependencyReflections=0.10.2
dependencySlf4j=2.0.13
# Test dependencies # Test dependencies
dependencyJunit=11.0-M2 dependencyJunit=11.0-M2

View file

@ -1,4 +1,5 @@
rootProject.name = 'sosengine' rootProject.name = 'sosengine'
include 'base' include 'base'
include 'slf4j-compat'
include 'testapp' include 'testapp'

49
slf4j-compat/build.gradle Normal file
View file

@ -0,0 +1,49 @@
plugins {
id 'java'
id 'io.freefair.lombok' version "${pluginLombok}"
}
dependencies {
// -> Runtime <-
// Lombok
compileOnly 'org.projectlombok:lombok:' + project.dependencyLombok
annotationProcessor 'org.projectlombok:lombok:' + project.dependencyLombok
// JetBrains Annotations
compileOnly 'org.jetbrains:annotations:' + project.dependencyJetbrainsAnnotations
// SLF4J
implementation 'org.slf4j:slf4j-api:' + project.dependencySlf4j
// -> Testing <-
// Jetbrains Annotations
testCompileOnly 'org.jetbrains:annotations:' + project.dependencyJetbrainsAnnotations
// JUnit
testImplementation platform('org.junit:junit-bom:5.' + project.dependencyJunit)
testImplementation 'org.junit.jupiter:junit-jupiter'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
// jOOR
testImplementation 'org.jooq:joor:' + project.dependencyJoor
// -> Project <-
implementation project(":base")
}
javadoc.options {
setMemberLevel(JavadocMemberLevel.PUBLIC)
setOverview("src/main/javadoc/overview.html")
setLocale("en_US")
setJFlags([
// Force Javadoc to use English translations
"-Duser.language=en_US"
])
}
test {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed"
}
}

1
slf4j-compat/gradle Symbolic link
View file

@ -0,0 +1 @@
../gradle

1
slf4j-compat/gradlew vendored Symbolic link
View file

@ -0,0 +1 @@
../gradlew

1
slf4j-compat/gradlew.bat vendored Symbolic link
View file

@ -0,0 +1 @@
../gradlew.bat

View file

@ -0,0 +1,229 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Contributors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.sosengine.slf4j_compat;
import de.staropensource.sosengine.base.Engine;
import de.staropensource.sosengine.base.EngineConfiguration;
import de.staropensource.sosengine.base.logging.LoggerInstance;
import de.staropensource.sosengine.base.types.CodePart;
import de.staropensource.sosengine.base.types.LogIssuer;
import de.staropensource.sosengine.base.types.LogLevel;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Marker;
import org.slf4j.event.Level;
import org.slf4j.event.LoggingEvent;
import org.slf4j.helpers.LegacyAbstractLogger;
import org.slf4j.helpers.MessageFormatter;
import org.slf4j.helpers.NormalizedParameters;
import java.io.Serial;
/**
* A SLF4J Logger that redirects all log calls to sos!engine's logger.
*
* @since 1-alpha0
*/
@SuppressWarnings({ "unused" })
public class CompatibilityLogger extends LegacyAbstractLogger {
/**
* Logger instance, used to print all log messages coming from SLF4J.
*
* @see LoggerInstance
* @since 1-alpha0
*/
private final LoggerInstance logger = new LoggerInstance(new LogIssuer(getClass(), CodePart.ENGINE));
/**
* Some funny serial number.
*
* @since 1-alpha0
*/
@Serial
private static final long serialVersionUID = -6092384602759788914L;
/**
* Determines if the logger has been initialized yet.
*
* @since 1-alpha0
*/
private static boolean INITIALIZED = false;
/**
* Lazily initializes the logger.
*
* @since 1-alpha0
*/
static void lazyInit() {
if (!INITIALIZED)
INITIALIZED = true;
}
/**
* Protected access allows only {@link CompatibilityLoggerFactory} and also derived classes to instantiate
* CompatibilityLogger instances.
*/
protected CompatibilityLogger(String name) {
this.name = name;
}
/**
* Initializes the logger.
*
* @since 1-alpha0
*/
// external software might be invoking this method directly. Do not rename
// or change its semantics.
static void init() {}
/**
* Returns the fully qualified caller name.
*
* @return null
*/
@Nullable
@Override
protected String getFullyQualifiedCallerName() {
return null;
}
/**
* Returns if {@code LogLevel.DIAGNOSTIC} is enabled.
*
* @return {@code LogLevel.DIAGNOSTIC} status
* @see LogLevel#DIAGNOSTIC
*/
public boolean isTraceEnabled() {
return isLevelAllowed(LogLevel.DIAGNOSTIC);
}
/**
* Returns if {@code LogLevel.VERBOSE} is enabled.
*
* @return {@code LogLevel.VERBOSE} status
* @see LogLevel#VERBOSE
*/
public boolean isDebugEnabled() {
return isLevelAllowed(LogLevel.VERBOSE);
}
/**
* Returns if {@code LogLevel.INFORMATIONAL} is enabled.
*
* @return {@code LogLevel.INFORMATIONAL} status
* @see LogLevel#INFORMATIONAL
*/
public boolean isInfoEnabled() {
return isLevelAllowed(LogLevel.INFORMATIONAL);
}
/**
* Returns if {@code LogLevel.WARNING} is enabled.
*
* @return {@code LogLevel.WARNING} status
* @see LogLevel#WARNING
*/
public boolean isWarnEnabled() {
return isLevelAllowed(LogLevel.WARNING);
}
/**
* Returns if {@code LogLevel.ERROR} is enabled.
*
* @return {@code LogLevel.ERROR} status
* @see LogLevel#ERROR
*/
public boolean isErrorEnabled() {
return isLevelAllowed(LogLevel.ERROR);
}
/**
* Just redirects to {@code forwardLogCall}.
*
* @param level the SLF4J level for this event
* @param marker The marker to be used for this event, may be null.
* @param messagePattern The message pattern which will be parsed and formatted
* @param arguments the array of arguments to be formatted, may be null
* @param throwable The exception whose stack trace should be logged, may be null
* @see CompatibilityLogger#forwardLogCall(Level, String, Object[], Throwable)
*/
@Override
protected void handleNormalizedLoggingCall(Level level, Marker marker, String messagePattern, Object[] arguments, Throwable throwable) {
forwardLogCall(level, messagePattern, arguments, throwable);
}
/**
* Logs something?
*
* @param event a {@link LoggingEvent}
*/
@SuppressWarnings("unused")
public void log(@NotNull LoggingEvent event) {
NormalizedParameters np = NormalizedParameters.normalize(event);
forwardLogCall(event.getLevel(), np.getMessage(), np.getArguments(), event.getThrowable());
}
/**
* Is the given log level currently enabled?
*
* @param level log level to check
* @return whether the logger is enabled for the given level
*/
protected boolean isLevelAllowed(LogLevel level) {
if (Engine.getInstance() == null || EngineConfiguration.getInstance() == null)
return false;
else
return EngineConfiguration.getInstance().getLoggerLevel().compareTo(level) < 0;
}
/**
* Forwards log calls from SLF4J to the StarOpenSource Engine.
*
* @param level log level, in sos!engine's style
* @param pattern message pattern
* @param arguments unused
* @param throwable throwable
*/
private void forwardLogCall(Level level, String pattern, Object[] arguments, Throwable throwable) {
String message = MessageFormatter.basicArrayFormat(pattern, arguments);
if (Engine.getInstance() == null) {
System.out.println(message);
return;
}
// Remove annoying Reflections messages
if (message.contains("could not create Vfs.Dir") || message.contains("Reflections took "))
return;
switch (level) {
case TRACE -> logger.diag(message);
case DEBUG -> logger.verb(message);
case INFO -> logger.info(message);
case WARN -> logger.warn(message);
case ERROR -> {
if (throwable == null)
logger.error(message);
else
logger.crash(message, throwable);
}
}
}
}

View file

@ -0,0 +1,56 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Contributors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.sosengine.slf4j_compat;
import org.slf4j.ILoggerFactory;
import org.slf4j.Logger;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* A factory for {@link CompatibilityLogger}.
*/
@SuppressWarnings({ "unused", "JavadocBlankLines" })
public class CompatibilityLoggerFactory implements ILoggerFactory {
ConcurrentMap<String, Logger> loggerMap;
public CompatibilityLoggerFactory() {
loggerMap = new ConcurrentHashMap<>();
CompatibilityLogger.lazyInit();
}
/**
* Return an appropriate {@link CompatibilityLogger} instance by name.
*
* This method will call {@link #createLogger(String)} if the logger
* has not been created yet.
*/
public Logger getLogger(String name) {
return loggerMap.computeIfAbsent(name, this::createLogger);
}
/**
* Actually creates the logger for the given name.
*/
protected Logger createLogger(String name) {
return new CompatibilityLogger(name);
}
}

View file

@ -0,0 +1,83 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Contributors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.sosengine.slf4j_compat;
import lombok.Getter;
import org.slf4j.ILoggerFactory;
import org.slf4j.IMarkerFactory;
import org.slf4j.helpers.BasicMarkerFactory;
import org.slf4j.helpers.NOPMDCAdapter;
import org.slf4j.spi.MDCAdapter;
import org.slf4j.spi.SLF4JServiceProvider;
/**
* Provider for {@link CompatibilityLogger}.
*
* @since 1-alpha0
*/
@SuppressWarnings({ "unused", "JavadocBlankLines" })
@Getter
public class CompatibilityLoggerProvider implements SLF4JServiceProvider {
/**
* Declare the version of the SLF4J API this implementation is compiled against.
* The value of this field is modified with each major release.
*
* @since 1-alpha0
*
* -- GETTER --
* {@inheritDoc}
*/
public String requestedApiVersion = "2.0.99";
/**
* Contains a {@link CompatibilityLoggerFactory}.
*
* @since 1-alpha0
*/
private ILoggerFactory loggerFactory;
/**
* Contains a {@link BasicMarkerFactory}.
*
* @since 1-alpha0
*
* -- GETTER --
* {@inheritDoc}
*/
private IMarkerFactory markerFactory;
/**
* Contains a {@link NOPMDCAdapter}.
*
* @since 1-alpha0
*
* -- GETTER --
* {@inheritDoc}
*/
private MDCAdapter mDCAdapter;
/** {@inheritDoc} */
@Override
public void initialize() {
loggerFactory = new CompatibilityLoggerFactory();
markerFactory = new BasicMarkerFactory();
mDCAdapter = new NOPMDCAdapter();
}
}

View file

@ -0,0 +1,67 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Contributors
* Licensed under the GNU Affero General Public License v3
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.sosengine.slf4j_compat;
import de.staropensource.sosengine.base.classes.SubsystemMainClass;
import de.staropensource.sosengine.base.logging.LoggerInstance;
import de.staropensource.sosengine.base.types.LogIssuer;
import lombok.Getter;
import org.slf4j.LoggerFactory;
/**
* Main object for the SLF4J Compatibility subsystem.
*
* @since 1-alpha0
*/
@SuppressWarnings({ "unused", "JavadocDeclaration", "JavadocBlankLines" })
public class Slf4jCompatibilitySubsystem implements SubsystemMainClass {
/**
* Instance.
*
* @since 1-alpha0
*
* -- GETTER --
* Returns the {@link Slf4jCompatibilitySubsystem} instance.
*
* @return {@link Slf4jCompatibilitySubsystem} instance unless uninitialized
* @since 1-alpha0
*/
@Getter
private static Slf4jCompatibilitySubsystem instance = null;
/** {@inheritDoc} */
private final LoggerInstance logger = new LoggerInstance(new LogIssuer(getClass()));
/**
* Constructor.
*/
public Slf4jCompatibilitySubsystem() {
// Check if subsystem has already initialized
if (instance == null)
instance = this;
else {
instance.logger.crash("The subsystem tried to initialize twice");
return;
}
LoggerFactory.getLogger(CompatibilityLogger.class).debug("If you see this then the SLF4J Compatibility Subsystem is working!");
logger.verb("Initialized subsystem");
}
}

View file

@ -0,0 +1 @@
de.staropensource.sosengine.slf4j_compat.CompatibilityLoggerProvider

View file

@ -16,12 +16,13 @@ dependencies {
// -> Project <- // -> Project <-
implementation project(":base") implementation project(":base")
implementation project(":slf4j-compat")
} }
javadoc.options { javadoc.options {
setMemberLevel(JavadocMemberLevel.PUBLIC) setMemberLevel(JavadocMemberLevel.PUBLIC)
setOverview("src/main/javadoc/overview.html") setOverview("src/main/javadoc/overview.html")
setLocale("en_US"); setLocale("en_US")
setJFlags([ setJFlags([
// Force Javadoc to use English translations // Force Javadoc to use English translations
"-Duser.language=en_US" "-Duser.language=en_US"

View file

@ -22,6 +22,7 @@ package de.staropensource.sosengine.testapp;
import de.staropensource.sosengine.base.Engine; import de.staropensource.sosengine.base.Engine;
import de.staropensource.sosengine.base.logging.LoggerInstance; import de.staropensource.sosengine.base.logging.LoggerInstance;
import de.staropensource.sosengine.base.types.LogIssuer; import de.staropensource.sosengine.base.types.LogIssuer;
import de.staropensource.sosengine.slf4j_compat.Slf4jCompatibilitySubsystem;
import lombok.Getter; import lombok.Getter;
/** /**
@ -91,13 +92,13 @@ public class Main {
// Say hello to the world! // Say hello to the world!
logger.info("Hello world!"); logger.info("Hello world!");
// Test logger levels // Initialize subsystems
logger.diag("Diagnostic message!"); new Slf4jCompatibilitySubsystem();
logger.verb("Verbose message!");
logger.sarn("Silent warning message!"); // Sleep for five seconds
logger.info("Informational message!"); Thread.sleep(5000);
logger.warn("Warning message!");
logger.error("Error message!"); // Shutdown
logger.crash("Crash message!"); Engine.getInstance().shutdown(0);
} }
} }