mirror of https://github.com/qos-ch/slf4j
ongoing work on the slf4j-jdk-platform-logging module
Signed-off-by: Ceki Gulcu <ceki@qos.ch>
This commit is contained in:
parent
e0448ad699
commit
a42ad1f4f7
|
|
@ -22,7 +22,7 @@
|
|||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
module org.slf4j.jdk {
|
||||
module org.slf4j.jdk.platform.logging {
|
||||
requires org.slf4j;
|
||||
provides java.lang.System.LoggerFinder with org.slf4j.jdk.SLF4JSystemLoggerFinder;
|
||||
}
|
||||
provides java.lang.System.LoggerFinder with org.slf4j.jdk.platform.logging.SLF4JSystemLoggerFinder;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,135 +0,0 @@
|
|||
package org.slf4j.jdk;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
/**
|
||||
* Adapts {@link Logger} to {@link System.Logger}.
|
||||
*/
|
||||
class SLF4JSystemLogger implements System.Logger {
|
||||
|
||||
private final Logger slf4jLogger;
|
||||
|
||||
public SLF4JSystemLogger(Logger logger) {
|
||||
this.slf4jLogger = requireNonNull(logger);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return slf4jLogger.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoggable(Level jplLevel) {
|
||||
switch (jplLevel) {
|
||||
case ALL:
|
||||
return true;
|
||||
case TRACE:
|
||||
return slf4jLogger.isTraceEnabled();
|
||||
case DEBUG:
|
||||
return slf4jLogger.isDebugEnabled();
|
||||
case INFO:
|
||||
return slf4jLogger.isInfoEnabled();
|
||||
case WARNING:
|
||||
return slf4jLogger.isWarnEnabled();
|
||||
case ERROR:
|
||||
return slf4jLogger.isErrorEnabled();
|
||||
case OFF:
|
||||
return false;
|
||||
default:
|
||||
reportUnknownLevel(jplLevel);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(Level jplLevel, ResourceBundle bundle, String msg, Throwable thrown) {
|
||||
String message = getResourceStringOrMessage(bundle, msg);
|
||||
switch (jplLevel) {
|
||||
case ALL:
|
||||
// fall-through intended because a message is visible on all log levels
|
||||
// if it is logged on the lowest level
|
||||
case TRACE:
|
||||
slf4jLogger.trace(message, thrown);
|
||||
break;
|
||||
case DEBUG:
|
||||
slf4jLogger.debug(message, thrown);
|
||||
break;
|
||||
case INFO:
|
||||
slf4jLogger.info(message, thrown);
|
||||
break;
|
||||
case WARNING:
|
||||
slf4jLogger.warn(message, thrown);
|
||||
break;
|
||||
case ERROR:
|
||||
slf4jLogger.error(message, thrown);
|
||||
break;
|
||||
case OFF:
|
||||
// don't do anything for a message on level `OFF`
|
||||
break;
|
||||
default:
|
||||
reportUnknownLevel(jplLevel);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(Level jplLevel, ResourceBundle bundle, String format, Object... params) {
|
||||
String message = getResourceStringOrMessage(bundle, format);
|
||||
switch (jplLevel) {
|
||||
case ALL:
|
||||
// fall-through intended because a message is visible on all log levels
|
||||
// if it is logged on the lowest level
|
||||
case TRACE:
|
||||
slf4jLogger.trace(message, params);
|
||||
break;
|
||||
case DEBUG:
|
||||
slf4jLogger.debug(message, params);
|
||||
break;
|
||||
case INFO:
|
||||
slf4jLogger.info(message, params);
|
||||
break;
|
||||
case WARNING:
|
||||
slf4jLogger.warn(message, params);
|
||||
break;
|
||||
case ERROR:
|
||||
slf4jLogger.error(message, params);
|
||||
break;
|
||||
case OFF:
|
||||
// don't do anything for a message on level `OFF`
|
||||
break;
|
||||
default:
|
||||
reportUnknownLevel(jplLevel);
|
||||
}
|
||||
}
|
||||
|
||||
private void reportUnknownLevel(Level jplLevel) {
|
||||
String message = "Unknown log level [" + jplLevel + "]";
|
||||
IllegalArgumentException iae = new IllegalArgumentException(message);
|
||||
org.slf4j.helpers.Util.report("Unsupported log level", iae);
|
||||
}
|
||||
|
||||
private static String getResourceStringOrMessage(ResourceBundle bundle, String msg) {
|
||||
if (bundle == null || msg == null)
|
||||
return msg;
|
||||
// ResourceBundle::getString throws:
|
||||
//
|
||||
// * NullPointerException for null keys
|
||||
// * ClassCastException if the message is no string
|
||||
// * MissingResourceException if there is no message for the key
|
||||
//
|
||||
// Handle all of these cases here to avoid log-related exceptions from crashing the JVM.
|
||||
try {
|
||||
return bundle.getString(msg);
|
||||
} catch (MissingResourceException ex) {
|
||||
return msg;
|
||||
} catch (ClassCastException ex) {
|
||||
return bundle.getObject(msg).toString();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
/**
|
||||
* Copyright (c) 2004-2021 QOS.ch
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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 org.slf4j.jdk.platform.logging;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import java.text.MessageFormat;
|
||||
import java.util.MissingResourceException;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.spi.LoggingEventBuilder;
|
||||
|
||||
/**
|
||||
* Adapts {@link Logger} to {@link System.Logger}.
|
||||
*/
|
||||
class SLF4JPlarformLogger implements System.Logger {
|
||||
|
||||
private final Logger slf4jLogger;
|
||||
|
||||
public SLF4JPlarformLogger(Logger logger) {
|
||||
this.slf4jLogger = requireNonNull(logger);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return slf4jLogger.getName();
|
||||
}
|
||||
|
||||
// The fact that non loggable levels (in java.lang.System.Logger.Level)
|
||||
// such as ALL and OFF leak into the public interface is quite a pity.
|
||||
|
||||
@Override
|
||||
public boolean isLoggable(Level jplLevel) {
|
||||
if (jplLevel == Level.ALL)
|
||||
return true;
|
||||
if (jplLevel == Level.OFF)
|
||||
return true;
|
||||
|
||||
org.slf4j.event.Level slf4jLevel = jplLevelToSLF4JLevel(jplLevel);
|
||||
|
||||
return slf4jLogger.isEnabledForLevel(slf4jLevel);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Transform a {@link Level} to {@link org.slf4j.event.Level}.
|
||||
*
|
||||
* This method assumes that Level.ALL or Level.OFF never reach this method.
|
||||
*
|
||||
* @param jplLevel
|
||||
* @return
|
||||
*/
|
||||
private org.slf4j.event.Level jplLevelToSLF4JLevel(Level jplLevel) {
|
||||
switch (jplLevel) {
|
||||
case TRACE:
|
||||
return org.slf4j.event.Level.TRACE;
|
||||
case DEBUG:
|
||||
return org.slf4j.event.Level.DEBUG;
|
||||
case INFO:
|
||||
return org.slf4j.event.Level.INFO;
|
||||
case WARNING:
|
||||
return org.slf4j.event.Level.WARN;
|
||||
case ERROR:
|
||||
return org.slf4j.event.Level.ERROR;
|
||||
default:
|
||||
reportUnknownLevel(jplLevel);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(Level jplLevel, ResourceBundle bundle, String msg, Throwable thrown) {
|
||||
log(jplLevel, bundle, msg, thrown, (Object[]) null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(Level jplLevel, ResourceBundle bundle, String format, Object... params) {
|
||||
log(jplLevel, bundle, format, null, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Single point of processing taking all possible paramets.
|
||||
*
|
||||
* @param jplLevel
|
||||
* @param bundle
|
||||
* @param msg
|
||||
* @param thrown
|
||||
* @param params
|
||||
*/
|
||||
private void log(Level jplLevel, ResourceBundle bundle, String msg, Throwable thrown, Object... params) {
|
||||
if (jplLevel == Level.OFF)
|
||||
return;
|
||||
|
||||
if (jplLevel == Level.ALL) {
|
||||
performLog(org.slf4j.event.Level.TRACE, bundle, msg, thrown, params);
|
||||
return;
|
||||
}
|
||||
|
||||
org.slf4j.event.Level slf4jLevel = jplLevelToSLF4JLevel(jplLevel);
|
||||
boolean isEnabled = slf4jLogger.isEnabledForLevel(slf4jLevel);
|
||||
|
||||
if (isEnabled) {
|
||||
performLog(slf4jLevel, bundle, msg, thrown, params);
|
||||
}
|
||||
}
|
||||
|
||||
private void performLog(org.slf4j.event.Level slf4jLevel, ResourceBundle bundle, String msg, Throwable thrown, Object... params) {
|
||||
String message = getResourceStringOrMessage(bundle, msg);
|
||||
LoggingEventBuilder leb = slf4jLogger.makeLoggingEventBuilder(slf4jLevel);
|
||||
if (thrown != null) {
|
||||
leb = leb.setCause(thrown);
|
||||
}
|
||||
if (params != null && params.length > 0) {
|
||||
// add the arguments to the logging event for possible processing by the backend
|
||||
for (Object p : params) {
|
||||
leb = leb.addArgument(p);
|
||||
}
|
||||
// The JDK uses a different formatting convention. We must invoke it now.
|
||||
message = String.format(message, params);
|
||||
}
|
||||
leb.log(message);
|
||||
}
|
||||
|
||||
private void reportUnknownLevel(Level jplLevel) {
|
||||
String message = "Unknown log level [" + jplLevel + "]";
|
||||
IllegalArgumentException iae = new IllegalArgumentException(message);
|
||||
org.slf4j.helpers.Util.report("Unsupported log level", iae);
|
||||
}
|
||||
|
||||
private static String getResourceStringOrMessage(ResourceBundle bundle, String msg) {
|
||||
if (bundle == null || msg == null)
|
||||
return msg;
|
||||
// ResourceBundle::getString throws:
|
||||
//
|
||||
// * NullPointerException for null keys
|
||||
// * ClassCastException if the message is no string
|
||||
// * MissingResourceException if there is no message for the key
|
||||
//
|
||||
// Handle all of these cases here to avoid log-related exceptions from crashing the JVM.
|
||||
try {
|
||||
return bundle.getString(msg);
|
||||
} catch (MissingResourceException ex) {
|
||||
return msg;
|
||||
} catch (ClassCastException ex) {
|
||||
return bundle.getObject(msg).toString();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/**
|
||||
* Copyright (c) 2004-2021 QOS.ch
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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 org.slf4j.jdk.platform.logging;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Manages instances of {@link SLF4JPlarformLogger}.
|
||||
*
|
||||
* @since 1.3.0
|
||||
* @author Ceki
|
||||
*
|
||||
*/
|
||||
public class SLF4JPlarformLoggerFactory {
|
||||
ConcurrentMap<String, SLF4JPlarformLogger> loggerMap = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Return an appropriate {@link SLF4JPlarformLogger} instance by name.
|
||||
*/
|
||||
public SLF4JPlarformLogger getLogger(String loggerName) {
|
||||
|
||||
|
||||
SLF4JPlarformLogger spla = loggerMap.get(loggerName);
|
||||
if (spla != null) {
|
||||
return spla;
|
||||
} else {
|
||||
Logger slf4jLogger = LoggerFactory.getLogger(loggerName);
|
||||
SLF4JPlarformLogger newInstance = new SLF4JPlarformLogger(slf4jLogger);
|
||||
SLF4JPlarformLogger oldInstance = loggerMap.putIfAbsent(loggerName, newInstance);
|
||||
return oldInstance == null ? newInstance : oldInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -22,17 +22,18 @@
|
|||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package org.slf4j.jdk;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
package org.slf4j.jdk.platform.logging;
|
||||
|
||||
/**
|
||||
* Uses SLF4J's {@link LoggerFactory#getLogger(String)} to get a logger
|
||||
* Uses {@link SLF4JPlarformLoggerFactory#getLogger(String)} to get a logger
|
||||
* that is adapted for {@link System.Logger}.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class SLF4JSystemLoggerFinder extends System.LoggerFinder {
|
||||
|
||||
final SLF4JPlarformLoggerFactory platformLoggerFactory = new SLF4JPlarformLoggerFactory();
|
||||
|
||||
@Override
|
||||
public System.Logger getLogger(String name, Module module) {
|
||||
// JEP 264[1], which introduced the Platform Logging API,
|
||||
|
|
@ -51,8 +52,8 @@ public class SLF4JSystemLoggerFinder extends System.LoggerFinder {
|
|||
// is updated to forward a module, we should do that here.
|
||||
//
|
||||
// [1] https://openjdk.java.net/jeps/264
|
||||
Logger slf4JLogger = LoggerFactory.getLogger(name);
|
||||
return new SLF4JSystemLogger(slf4JLogger);
|
||||
SLF4JPlarformLogger adapter = platformLoggerFactory.getLogger(name);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,6 +1,9 @@
|
|||
Implementation-Title: slf4j-ext
|
||||
Implementation-Title: slf4j-jdk-platform-logging
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-SymbolicName: slf4j.ext
|
||||
Bundle-SymbolicName: slf4j.jdk.platform.logging
|
||||
Bundle-Name: slf4j-jdk-platform-logging
|
||||
Bundle-Vendor: SLF4J.ORG
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
|
||||
Require-Bundle: slf4j.api
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-9
|
||||
Export-Package: slf4j.jdk.platform.logging;version=${parsedVersion.osgiVersion}
|
||||
Import-Package: org.slf4j;version=${parsedVersion.osgiVersion}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
org.slf4j.jdk.SLF4JSystemLoggerFinder
|
||||
package org.slf4j.jdk.platform.logging.SLF4JSystemLoggerFinder
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
package org.slf4j.jdk;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.System.LoggerFinder;
|
||||
import java.util.Optional;
|
||||
import java.util.ServiceConfigurationError;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.ServiceLoader.Provider;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
@Ignore
|
||||
public class SLF4JSystemLoggerTest {
|
||||
|
||||
private static final PrintStream ERROR_OUT = System.err;
|
||||
private static final ByteArrayOutputStream OUTPUT = new ByteArrayOutputStream();
|
||||
private static final PrintStream ERROR_OUT_REPLACEMENT = new PrintStream(OUTPUT);
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
System.setErr(ERROR_OUT_REPLACEMENT);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
System.setErr(ERROR_OUT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loggerFinderLoadedAsService() {
|
||||
// this method asserts that a `LoggerFinder` of the correct type was loaded
|
||||
getSLF4JSystemLoggerFinder();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loggerFinderReturnsLogger() {
|
||||
System.Logger logger = getSLF4JSystemLogger();
|
||||
assertNotNull("LoggerFinder must return logger", logger);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loggerLogsMessage() {
|
||||
System.Logger logger = getSLF4JSystemLogger();
|
||||
String message = "Test system logging!";
|
||||
logger.log(System.Logger.Level.INFO, message);
|
||||
|
||||
String output = getOutput();
|
||||
|
||||
assertTrue("Captured output should contain logged message.", output.contains(message));
|
||||
}
|
||||
|
||||
private static System.LoggerFinder getSLF4JSystemLoggerFinder() {
|
||||
// this fails when test is executed from the module path
|
||||
// because the module declaration does not declare
|
||||
// `uses System.LoggerFinder`
|
||||
|
||||
ServiceLoader<System.LoggerFinder> loader = ServiceLoader.load(System.LoggerFinder.class);
|
||||
Optional<Provider<LoggerFinder>> optional = loader.stream()
|
||||
.filter( provider -> SLF4JSystemLoggerFinder.class.isAssignableFrom(provider.type()))
|
||||
.findAny();
|
||||
|
||||
if(optional.isPresent()) {
|
||||
Provider<LoggerFinder> p = optional.get();
|
||||
return p.get();
|
||||
} else {
|
||||
throw new ServiceConfigurationError("Could not find SLF4JSystemLoggerFinder");
|
||||
}
|
||||
//
|
||||
// return .stream()
|
||||
// .filter(finderProvider -> SLF4JSystemLoggerFinder.class.isAssignableFrom(finderProvider.type()))
|
||||
// .findAny()
|
||||
// .map(ServiceLoader.Provider::get)
|
||||
// .orElseThrow();
|
||||
}
|
||||
|
||||
private static System.Logger getSLF4JSystemLogger() {
|
||||
LoggerFinder loggerFinder = getSLF4JSystemLoggerFinder();
|
||||
return loggerFinder.getLogger("TestLogger", SLF4JSystemLoggerTest.class.getModule());
|
||||
}
|
||||
|
||||
private static String getOutput() {
|
||||
ERROR_OUT_REPLACEMENT.flush();
|
||||
return OUTPUT.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -22,7 +22,7 @@
|
|||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
package org.slf4j.jdk;
|
||||
package org.slf4j.jdk.platform.logging;
|
||||
|
||||
import java.lang.System.Logger;
|
||||
|
||||
|
|
@ -31,7 +31,7 @@ import java.lang.System.LoggerFinder;
|
|||
|
||||
import org.junit.Test;
|
||||
|
||||
public class PlatformLoggingTest {
|
||||
public class SLF4JPlatformLoggingTest {
|
||||
|
||||
|
||||
@Test
|
||||
|
|
@ -39,5 +39,6 @@ public class PlatformLoggingTest {
|
|||
LoggerFinder finder = System.LoggerFinder.getLoggerFinder();
|
||||
Logger systemLogger = finder.getLogger("x", null);
|
||||
systemLogger.log(Level.INFO, "hello");
|
||||
systemLogger.log(Level.INFO, "hello %s", "world");
|
||||
}
|
||||
}
|
||||
|
|
@ -900,7 +900,12 @@ org.slf4j.impl.StaticLoggerBinder.SINGLETON from class org.slf4j.LoggerFactory
|
|||
<td>log4j-over-slf4j.jar</td>
|
||||
<td>log4j</td>
|
||||
</tr>
|
||||
|
||||
|
||||
<tr>
|
||||
<td>slf4j-jdk-platform-logging.jar</td>
|
||||
<td>org.slf4j.jdk.platform.logging</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
</dd>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue