testing SLF4J-353 support

This commit is contained in:
Ceki Gulcu 2016-02-09 16:34:46 +01:00
parent c3751df9af
commit 2d4074c65a
23 changed files with 774 additions and 81 deletions

17
integration/build.xml Normal file → Executable file
View File

@ -56,7 +56,7 @@
<pathelement location="../slf4j-simple/target/slf4j-simple-${currentVersion}.jar" />
</path >
<path id="incompatibleMultiBinding">
<pathelement location="target/test-classes/" />
<pathelement location="../slf4j-api/target/slf4j-api-${currentVersion}.jar" />
@ -103,7 +103,7 @@
<target name="testAll" depends="init,
testMissingSingletonMethod,
testMismatch,
testMismatch,
testMatch,
testMultiBinding,
testIncompatibleMultiBinding,
@ -132,6 +132,19 @@
</target>
<target name="testMixed17">
<junit printsummary="yes" fork="no" haltonfailure="yes">
<classpath refid="pathMixed17" />
<formatter type="plain" />
<test fork="yes" todir="target/unit-reports"
outfile="TEST-MIXED-17"
name="org.slf4j.VersionMismatchAssertionTest" />
</junit>
</target>
<target name="testMismatch">
<junit printsummary="yes" fork="no" haltonfailure="yes">

View File

@ -1,5 +1,5 @@
/**
* Copyright (c) 2004-2011 QOS.ch
* Copyright (c) 2004-2016 QOS.ch
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining

View File

@ -182,9 +182,10 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.10</version>
<version>2.19.1</version>
<configuration>
<forkMode>once</forkMode>
<forkCount>2</forkCount>
<reuseForks>false</reuseForks>
<reportFormat>plain</reportFormat>
<trimStackTrace>false</trimStackTrace>
<excludes>

View File

@ -32,7 +32,6 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.slf4j.event.LoggingEventAware;
import org.slf4j.event.SubstituteLoggingEvent;
import org.slf4j.helpers.NOPLoggerFactory;
import org.slf4j.helpers.SubstituteLogger;
@ -70,6 +69,7 @@ public final class LoggerFactory {
static final String VERSION_MISMATCH = CODES_PREFIX + "#version_mismatch";
static final String SUBSTITUTE_LOGGER_URL = CODES_PREFIX + "#substituteLogger";
static final String LOGGER_NAME_MISMATCH_URL = CODES_PREFIX + "#loggerNameMismatch";
static final String REPLAY_URL = CODES_PREFIX + "#replay";
static final String UNSUCCESSFUL_INIT_URL = CODES_PREFIX + "#unsuccessfulInit";
static final String UNSUCCESSFUL_INIT_MSG = "org.slf4j.LoggerFactory could not be successfully initialized. See also " + UNSUCCESSFUL_INIT_URL;
@ -116,7 +116,6 @@ public final class LoggerFactory {
*/
static void reset() {
INITIALIZATION_STATE = UNINITIALIZED;
SUBST_FACTORY = new SubstituteLoggerFactory();
}
private final static void performInitialization() {
@ -146,6 +145,7 @@ public final class LoggerFactory {
reportActualBinding(staticLoggerBinderPathSet);
fixSubstitutedLoggers();
playRecordedEvents();
SUBST_FACTORY.clear();
} catch (NoClassDefFoundError ncde) {
String msg = ncde.getMessage();
if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
@ -180,10 +180,21 @@ public final class LoggerFactory {
private static void playRecordedEvents() {
List<SubstituteLoggingEvent> events = SUBST_FACTORY.getEventList();
for (SubstituteLoggingEvent event : events) {
if (events.isEmpty()) {
return;
}
for (int i = 0; i < events.size(); i++) {
SubstituteLoggingEvent event = events.get(i);
SubstituteLogger substLogger = event.getLogger();
if (substLogger.isDelegateEventAware()) {
if (i == 0)
emitReplayWarning(events.size());
substLogger.log(event);
} else {
if(i == 0)
emitSubstitutionWarning();
Util.report(substLogger.getName());
}
}
}
@ -195,25 +206,10 @@ public final class LoggerFactory {
return;
}
boolean substitutionWarningEmmitted = false;
for (SubstituteLogger subLogger : loggers) {
Logger logger = getLogger(subLogger.getName());
subLogger.setDelegate(logger);
if (!isEventAware(logger)) {
if (!substitutionWarningEmmitted) {
emitSubstitutionWarning();
substitutionWarningEmmitted = true;
}
Util.report(subLogger.getName());
}
}
SUBST_FACTORY.clear();
}
private static boolean isEventAware(Logger logger) {
return logger instanceof LoggingEventAware;
}
private static void emitSubstitutionWarning() {
@ -224,6 +220,12 @@ public final class LoggerFactory {
Util.report("See also " + SUBSTITUTE_LOGGER_URL);
}
private static void emitReplayWarning(int eventCount) {
Util.report("A number (" + eventCount + ") of logging calls during the initialization phase have been intercepted and are");
Util.report("now being replayed. These are suject to the filtering rules of the underlying logging system.");
Util.report("See also " + REPLAY_URL);
}
private final static void versionSanityCheck() {
try {
String requested = StaticLoggerBinder.REQUESTED_API_VERSION;
@ -375,6 +377,7 @@ public final class LoggerFactory {
}
}
}
switch (INITIALIZATION_STATE) {
case SUCCESSFUL_INITIALIZATION:
return StaticLoggerBinder.getSingleton().getLoggerFactory();

View File

@ -4,14 +4,17 @@ import java.util.List;
import org.slf4j.Logger;
import org.slf4j.Marker;
import org.slf4j.helpers.SubstituteLogger;
public class EventRecodingLogger implements Logger {
String name;
SubstituteLogger logger;
List<SubstituteLoggingEvent> eventList;
public EventRecodingLogger(String name, List<SubstituteLoggingEvent> eventList) {
this.name = name;
public EventRecodingLogger(SubstituteLogger logger, List<SubstituteLoggingEvent> eventList) {
this.logger = logger;
this.name = logger.getName();
this.eventList = eventList;
}
@ -24,9 +27,14 @@ public class EventRecodingLogger implements Logger {
}
private void recordEvent(Level level, Marker marker, String msg, Object[] args, Throwable throwable) {
//System.out.println("recording logger:"+name+", msg:"+msg);
SubstituteLoggingEvent loggingEvent = new SubstituteLoggingEvent();
loggingEvent.setTimeStamp(System.currentTimeMillis());
loggingEvent.setLevel(level);
loggingEvent.setLogger(logger);
loggingEvent.setLoggerName(name);
loggingEvent.setMessage(msg);
loggingEvent.setArgumentArray(args);
loggingEvent.setThrowable(throwable);

View File

@ -1,10 +0,0 @@
package org.slf4j.event;
/**
*
* @author Ceki Gulcu
* @since 1.7.15
*/
public interface LoggingEventAware {
void log(LoggingEvent event);
}

View File

@ -24,11 +24,12 @@
*/
package org.slf4j.helpers;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.Marker;
import org.slf4j.event.LoggingEventAware;
import org.slf4j.event.EventRecodingLogger;
import org.slf4j.event.LoggingEvent;
import org.slf4j.event.SubstituteLoggingEvent;
@ -43,13 +44,15 @@ import org.slf4j.event.SubstituteLoggingEvent;
* @author Chetan Mehrotra
* @author Ceki Gulcu
*/
public class SubstituteLogger implements Logger, LoggingEventAware {
public class SubstituteLogger implements Logger {
private final String name;
private volatile Logger _delegate;
List<SubstituteLoggingEvent> eventList;
private EventRecodingLogger eventRecodingLogger;
List<SubstituteLoggingEvent> eventList;
public SubstituteLogger(String name, List<SubstituteLoggingEvent> eventList) {
this.name = name;
this.eventList = eventList;
@ -324,11 +327,15 @@ public class SubstituteLogger implements Logger, LoggingEventAware {
* instance.
*/
Logger delegate() {
return _delegate != null ? _delegate : makeEventRecodingLogger();
return _delegate != null ? _delegate : getEventRecordingLogger();
}
private EventRecodingLogger makeEventRecodingLogger() {
return new EventRecodingLogger(name, eventList);
private Logger getEventRecordingLogger() {
if(eventRecodingLogger == null) {
eventRecodingLogger = new EventRecodingLogger(this, eventList);
}
return eventRecodingLogger;
}
/**
@ -339,14 +346,30 @@ public class SubstituteLogger implements Logger, LoggingEventAware {
this._delegate = delegate;
}
Boolean delegateEventAware;
Method logMethodCache;
public boolean isDelegateEventAware() {
return (_delegate instanceof LoggingEventAware);
}
public void log(LoggingEvent event) {
if(_delegate instanceof LoggingEventAware) {
LoggingEventAware eventLogger = (LoggingEventAware) _delegate;
eventLogger.log(event);
if (delegateEventAware != null)
return delegateEventAware;
try {
logMethodCache = _delegate.getClass().getMethod("log", LoggingEvent.class);
delegateEventAware = Boolean.TRUE;
} catch (NoSuchMethodException e) {
delegateEventAware = Boolean.FALSE;
}
}
return delegateEventAware;
}
public void log(LoggingEvent event) {
if (isDelegateEventAware()) {
try {
logMethodCache.invoke(_delegate, event);
} catch (IllegalAccessException e) {
} catch (IllegalArgumentException e) {
} catch (InvocationTargetException e) {
}
}
}
}

View File

@ -71,5 +71,6 @@ public class SubstituteLoggerFactory implements ILoggerFactory {
public void clear() {
loggers.clear();
eventList.clear();
}
}

View File

@ -31,7 +31,6 @@ import org.slf4j.Logger;
import org.slf4j.Marker;
import org.slf4j.event.EventConstants;
import org.slf4j.event.LoggingEvent;
import org.slf4j.event.LoggingEventAware;
import org.slf4j.helpers.FormattingTuple;
import org.slf4j.helpers.MarkerIgnoringBase;
import org.slf4j.helpers.MessageFormatter;
@ -46,7 +45,7 @@ import org.slf4j.spi.LocationAwareLogger;
* @author Ceki G&uuml;lc&uuml;
* @author Peter Royal
*/
public final class JDK14LoggerAdapter extends MarkerIgnoringBase implements LocationAwareLogger, LoggingEventAware {
public final class JDK14LoggerAdapter extends MarkerIgnoringBase implements LocationAwareLogger {
private static final long serialVersionUID = -8053026990503422791L;

View File

@ -0,0 +1,7 @@
package org.slf4j;
public class LoggerFactoryFriend {
static public void reset() {
LoggerFactory.reset();
}
}

View File

@ -0,0 +1,152 @@
/**
* Copyright (c) 2004-2016 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.impl;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.util.List;
import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Handler;
import java.util.logging.LogManager;
import java.util.logging.LogRecord;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MultithreadedInitializationTest {
final static int THREAD_COUNT = 4 + Runtime.getRuntime().availableProcessors() * 2;
private static AtomicLong EVENT_COUNT = new AtomicLong(0);
final CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT + 1);
int diff = new Random().nextInt(10000);
String loggerName = "org.slf4j.impl.MultithreadedInitializationTest";
private static java.util.logging.Logger getRootLogger() {
return LogManager.getLogManager().getLogger("");
}
@After
public void tearDown() throws Exception {
java.util.logging.Logger rootLogger = getRootLogger();
Handler[] handlers = rootLogger.getHandlers();
for (int i = 0; i < handlers.length; i++) {
if (handlers[i] instanceof RecordingHandler) {
rootLogger.removeHandler(handlers[i]);
}
}
}
@Before
public void addRecordingHandler() {
getRootLogger().addHandler(new RecordingHandler());
}
@Test
public void multiThreadedInitialization() throws InterruptedException, BrokenBarrierException {
System.out.println("THREAD_COUNT=" + THREAD_COUNT);
LoggerAccessingThread[] accessors = harness();
for (LoggerAccessingThread accessor : accessors) {
EVENT_COUNT.getAndIncrement();
accessor.logger.info("post harness");
}
Logger logger = LoggerFactory.getLogger(loggerName + ".slowInitialization-" + diff);
logger.info("hello");
EVENT_COUNT.getAndIncrement();
List<LogRecord> records = getRecordedEvents();
assertEquals(EVENT_COUNT.get(), records.size());
}
private List<LogRecord> getRecordedEvents() {
RecordingHandler ra = findRecordingHandler();
if(ra == null) {
fail("failed to fing RecordingHandler");
}
return ra.records;
}
RecordingHandler findRecordingHandler() {
java.util.logging.Logger root = LogManager.getLogManager().getLogger("");
Handler[] handlers = root.getHandlers();
for (Handler h : handlers) {
if (h instanceof RecordingHandler)
return (RecordingHandler) h;
}
return null;
}
private static LoggerAccessingThread[] harness() throws InterruptedException, BrokenBarrierException {
LoggerAccessingThread[] threads = new LoggerAccessingThread[THREAD_COUNT];
final CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT + 1);
for (int i = 0; i < THREAD_COUNT; i++) {
threads[i] = new LoggerAccessingThread(barrier, i);
threads[i].start();
}
barrier.await();
for (int i = 0; i < THREAD_COUNT; i++) {
threads[i].join();
}
return threads;
}
static class LoggerAccessingThread extends Thread {
final CyclicBarrier barrier;
Logger logger;
int count;
LoggerAccessingThread(CyclicBarrier barrier, int count) {
this.barrier = barrier;
this.count = count;
}
public void run() {
try {
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
logger = LoggerFactory.getLogger(this.getClass().getName() + "-" + count);
logger.info("in run method");
EVENT_COUNT.getAndIncrement();
}
};
}

View File

@ -0,0 +1,25 @@
package org.slf4j.impl;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Handler;
import java.util.logging.LogRecord;
public class RecordingHandler extends Handler {
List<LogRecord> records = new ArrayList<LogRecord>();
@Override
public void publish(LogRecord record) {
records.add(record);
}
@Override
public void flush() {
}
@Override
public void close() throws SecurityException {
}
}

View File

@ -28,16 +28,12 @@ import static org.slf4j.event.EventConstants.NA_SUBST;
import java.io.Serializable;
import org.apache.log4j.Category;
import org.apache.log4j.Level;
import org.apache.log4j.Priority;
import org.apache.log4j.spi.LocationInfo;
import org.apache.log4j.spi.ThrowableInformation;
import org.slf4j.Logger;
import org.slf4j.Marker;
import org.slf4j.event.EventConstants;
import org.slf4j.event.LoggingEvent;
import org.slf4j.event.LoggingEventAware;
import org.slf4j.helpers.FormattingTuple;
import org.slf4j.helpers.MarkerIgnoringBase;
import org.slf4j.helpers.MessageFormatter;
@ -61,7 +57,7 @@ import org.slf4j.spi.LocationAwareLogger;
*
* @author Ceki G&uuml;lc&uuml;
*/
public final class Log4jLoggerAdapter extends MarkerIgnoringBase implements LocationAwareLogger, LoggingEventAware, Serializable {
public final class Log4jLoggerAdapter extends MarkerIgnoringBase implements LocationAwareLogger, Serializable {
private static final long serialVersionUID = 6182834493563598289L;

View File

@ -0,0 +1,124 @@
/**
* Copyright (c) 2004-2011 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.impl;
import static org.junit.Assert.assertEquals;
import java.util.List;
import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.log4j.LogManager;
import org.apache.log4j.spi.LoggingEvent;
import org.junit.After;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MultithreadedInitializationTest {
// value of LogManager.DEFAULT_CONFIGURATION_KEY;
static String CONFIG_FILE_KEY = "log4j.configuration";
final static int THREAD_COUNT = 4 + Runtime.getRuntime().availableProcessors() * 2;
private static AtomicLong EVENT_COUNT = new AtomicLong(0);
final CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT + 1);
int diff = new Random().nextInt(10000);
String loggerName = "org.slf4j.impl.RecursiveInitializationTest";
@After
public void tearDown() throws Exception {
System.clearProperty(CONFIG_FILE_KEY);
}
@Test
public void multiThreadedInitialization() throws InterruptedException, BrokenBarrierException {
System.out.println("THREAD_COUNT=" + THREAD_COUNT);
System.setProperty(CONFIG_FILE_KEY, "recursiveInitWithActivationDelay.properties");
LoggerAccessingThread[] accessors = harness();
for (LoggerAccessingThread accessor : accessors) {
EVENT_COUNT.getAndIncrement();
accessor.logger.info("post harness");
}
Logger logger = LoggerFactory.getLogger(loggerName + ".slowInitialization-" + diff);
logger.info("hello");
EVENT_COUNT.getAndIncrement();
List<LoggingEvent> events = getRecordedEvents();
assertEquals(EVENT_COUNT.get(), events.size());
}
private List<LoggingEvent> getRecordedEvents() {
org.apache.log4j.Logger root = LogManager.getRootLogger();
RecursiveAppender ra = (RecursiveAppender) root.getAppender("RECURSIVE");
return ra.events;
}
private static LoggerAccessingThread[] harness() throws InterruptedException, BrokenBarrierException {
LoggerAccessingThread[] threads = new LoggerAccessingThread[THREAD_COUNT];
final CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT + 1);
for (int i = 0; i < THREAD_COUNT; i++) {
threads[i] = new LoggerAccessingThread(barrier, i);
threads[i].start();
}
barrier.await();
for (int i = 0; i < THREAD_COUNT; i++) {
threads[i].join();
}
return threads;
}
static class LoggerAccessingThread extends Thread {
final CyclicBarrier barrier;
Logger logger;
int count;
LoggerAccessingThread(CyclicBarrier barrier, int count) {
this.barrier = barrier;
this.count = count;
}
public void run() {
try {
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
logger = LoggerFactory.getLogger(this.getClass().getName()+"-"+count);
logger.info("in run method");
EVENT_COUNT.getAndIncrement();
}
};
}

View File

@ -24,6 +24,8 @@
*/
package org.slf4j.impl;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import org.apache.log4j.AppenderSkeleton;
@ -34,15 +36,20 @@ import org.slf4j.LoggerFactory;
public class RecursiveAppender extends AppenderSkeleton {
int diff = new Random().nextInt();
int activationDelay = 0;
String loggerName = "org.slf4j.impl.RecursiveAppender" + diff;
List<LoggingEvent> events = new ArrayList<LoggingEvent>();
public RecursiveAppender() {
System.out.println("in RecursiveAppender constructor");
Logger logger = LoggerFactory.getLogger("RecursiveAppender" + diff);
System.out.println("logger class=" + logger.getClass().getName());
System.out.println("entering RecursiveAppender constructor");
Logger logger = LoggerFactory.getLogger(loggerName);
logger.info("Calling a logger in the constructor");
System.out.println("exiting RecursiveAppender constructor");
}
protected void append(LoggingEvent arg0) {
protected void append(LoggingEvent e) {
events.add(e);
}
public void close() {
@ -51,4 +58,32 @@ public class RecursiveAppender extends AppenderSkeleton {
public boolean requiresLayout() {
return false;
}
@Override
public void activateOptions() {
System.out.println("entering RecursiveAppender.activateOptions");
if(activationDelay > 0) {
Logger logger = LoggerFactory.getLogger(loggerName);
logger.info("About to wait {} millis", activationDelay);
try {
Thread.sleep(activationDelay);
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.info("Done waiting {} millis", activationDelay);
}
super.activateOptions();
System.out.println("exiting RecursiveAppender.activateOptions");
}
public int getActivationDelay() {
return activationDelay;
}
public void setActivationDelay(int activationDelay) {
this.activationDelay = activationDelay;
}
}

View File

@ -27,21 +27,17 @@ package org.slf4j.impl;
import java.util.Random;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RecursiveInitializationTest {
// value of LogManager.DEFAULT_CONFIGURATION_KEY;
static String CONFIG_FILE_KEY = "log4j.configuration";
int diff = new Random().nextInt(10000);
@Before
public void setUp() throws Exception {
System.setProperty(CONFIG_FILE_KEY, "recursiveInit.properties");
}
String loggerName = "org.slf4j.impl.RecursiveInitializationTest";
@After
public void tearDown() throws Exception {
@ -49,9 +45,9 @@ public class RecursiveInitializationTest {
}
@Test
public void testLog4j() {
Logger logger = LoggerFactory.getLogger("x" + diff);
System.out.println("logger class=" + logger.getClass().getName());
public void loggingDuringInitialization() {
System.setProperty(CONFIG_FILE_KEY, "recursiveInit.properties");
Logger logger = LoggerFactory.getLogger(loggerName+".loggingDuringInitialization-"+diff);
logger.info("hello");
}

View File

@ -0,0 +1,9 @@
log4j.debug=true
log4j.rootLogger=DEBUG, CONSOLE, RECURSIVE
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=CON %d [%t] %c - %m%n
log4j.appender.RECURSIVE=org.slf4j.impl.RecursiveAppender
log4j.appender.RECURSIVE.activationDelay=10

View File

@ -0,0 +1,7 @@
package org.slf4j;
public class LoggerFactoryFriend {
static public void reset() {
LoggerFactory.reset();
}
}

View File

@ -0,0 +1,118 @@
/**
* Copyright (c) 2004-2016 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.impl;
import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.atomic.AtomicLong;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.LoggerFactoryFriend;
public class MultithreadedInitializationTest {
final static int THREAD_COUNT = 4 + Runtime.getRuntime().availableProcessors() * 2;
private static AtomicLong EVENT_COUNT = new AtomicLong(0);
final CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT + 1);
int diff = new Random().nextInt(10000);
String loggerName = "org.slf4j.impl.MultithreadedInitializationTest";
@Before
public void setup() {
LoggerFactoryFriend.reset();
}
@After
public void tearDown() throws Exception {
LoggerFactoryFriend.reset();
}
@Test
public void multiThreadedInitialization() throws InterruptedException, BrokenBarrierException {
System.out.println("THREAD_COUNT=" + THREAD_COUNT);
LoggerAccessingThread[] accessors = harness();
for (LoggerAccessingThread accessor : accessors) {
EVENT_COUNT.getAndIncrement();
accessor.logger.info("post harness");
}
Logger logger = LoggerFactory.getLogger(loggerName + ".slowInitialization-" + diff);
logger.info("hello");
EVENT_COUNT.getAndIncrement();
int NUM_LINES_IN_SLF4J_REPLAY_WARNING=3;
//assertEquals(EVENT_COUNT.get()+NUM_LINES_IN_SLF4J_REPLAY_WARNING, sps.stringList.size());
}
private static LoggerAccessingThread[] harness() throws InterruptedException, BrokenBarrierException {
LoggerAccessingThread[] threads = new LoggerAccessingThread[THREAD_COUNT];
final CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT + 1);
for (int i = 0; i < THREAD_COUNT; i++) {
threads[i] = new LoggerAccessingThread(barrier, i);
threads[i].start();
}
barrier.await();
for (int i = 0; i < THREAD_COUNT; i++) {
threads[i].join();
}
return threads;
}
static class LoggerAccessingThread extends Thread {
final CyclicBarrier barrier;
Logger logger;
int count;
LoggerAccessingThread(CyclicBarrier barrier, int count) {
this.barrier = barrier;
this.count = count;
}
public void run() {
try {
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
logger = LoggerFactory.getLogger(this.getClass().getName() + "-" + count);
logger.info("in run method");
EVENT_COUNT.getAndIncrement();
}
};
}

View File

@ -36,6 +36,7 @@ import java.util.Date;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.event.LoggingEvent;
import org.slf4j.helpers.FormattingTuple;
import org.slf4j.helpers.MarkerIgnoringBase;
import org.slf4j.helpers.MessageFormatter;
@ -645,4 +646,14 @@ public class SimpleLogger extends MarkerIgnoringBase {
public void error(String msg, Throwable t) {
log(LOG_LEVEL_ERROR, msg, t);
}
public void log(LoggingEvent event) {
int levelInt = event.getLevel().toInt();
if (!isLevelEnabled(levelInt)) {
return;
}
FormattingTuple tp = MessageFormatter.arrayFormat(event.getMessage(), event.getArgumentArray(), event.getThrowable());
log(levelInt, tp.getMessage(), event.getThrowable());
}
}

View File

@ -0,0 +1,7 @@
package org.slf4j;
public class LoggerFactoryFriend {
static public void reset() {
LoggerFactory.reset();
}
}

View File

@ -0,0 +1,151 @@
/**
* Copyright (c) 2004-2016 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.impl;
import static org.junit.Assert.assertEquals;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.atomic.AtomicLong;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.LoggerFactoryFriend;
public class MultithreadedInitializationTest {
final static int THREAD_COUNT = 4 + Runtime.getRuntime().availableProcessors() * 2;
private static AtomicLong EVENT_COUNT = new AtomicLong(0);
private final PrintStream oldErr = System.err;
final CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT + 1);
int diff = new Random().nextInt(10000);
String loggerName = "org.slf4j.impl.MultithreadedInitializationTest";
StringPrintStream sps = new StringPrintStream(oldErr);
@Before
public void setup() {
LoggerFactoryFriend.reset();
System.setErr(sps);
}
@After
public void tearDown() throws Exception {
LoggerFactoryFriend.reset();
System.setErr(oldErr);
}
@Test
public void multiThreadedInitialization() throws InterruptedException, BrokenBarrierException {
System.out.println("THREAD_COUNT=" + THREAD_COUNT);
LoggerAccessingThread[] accessors = harness();
for (LoggerAccessingThread accessor : accessors) {
EVENT_COUNT.getAndIncrement();
accessor.logger.info("post harness");
}
Logger logger = LoggerFactory.getLogger(loggerName + ".slowInitialization-" + diff);
logger.info("hello");
EVENT_COUNT.getAndIncrement();
int NUM_LINES_IN_SLF4J_REPLAY_WARNING=3;
assertEquals(EVENT_COUNT.get()+NUM_LINES_IN_SLF4J_REPLAY_WARNING, sps.stringList.size());
}
private static LoggerAccessingThread[] harness() throws InterruptedException, BrokenBarrierException {
LoggerAccessingThread[] threads = new LoggerAccessingThread[THREAD_COUNT];
final CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT + 1);
for (int i = 0; i < THREAD_COUNT; i++) {
threads[i] = new LoggerAccessingThread(barrier, i);
threads[i].start();
}
barrier.await();
for (int i = 0; i < THREAD_COUNT; i++) {
threads[i].join();
}
return threads;
}
static class LoggerAccessingThread extends Thread {
final CyclicBarrier barrier;
Logger logger;
int count;
LoggerAccessingThread(CyclicBarrier barrier, int count) {
this.barrier = barrier;
this.count = count;
}
public void run() {
try {
barrier.await();
} catch (Exception e) {
e.printStackTrace();
}
logger = LoggerFactory.getLogger(this.getClass().getName() + "-" + count);
logger.info("in run method");
EVENT_COUNT.getAndIncrement();
}
};
public static class StringPrintStream extends PrintStream {
public static final String LINE_SEP = System.getProperty("line.separator");
PrintStream other;
List<String> stringList = new ArrayList<String>();
public StringPrintStream(PrintStream ps) {
super(ps);
other = ps;
}
public void print(String s) {
other.print(s);
stringList.add(s);
}
public void println(String s) {
other.println(s);
stringList.add(s);
}
public void println(Object o) {
other.println(o);
stringList.add(o.toString());
}
};
}

View File

@ -441,9 +441,9 @@ class B extends A {
</p>
<p>However, as reported in <a
href="http://bugzilla.slf4j.org/show_bug.cgi?id=68">bug 68</a>, in
some environments it may be difficult to upgrade the log4j
version. To accommodate such circumstances, SLF4J's
href="http://jira.qos.ch/browse/SLF4J-59">issue 59</a>, in some
environments it may be difficult to upgrade the log4j version. To
accommodate such circumstances, SLF4J's
<code>Log4jLoggerAdapter</code> will map the TRACE level as
DEBUG.</p>
@ -452,13 +452,13 @@ class B extends A {
were created during the default configuration phase of the
underlying logging system</h3>
<p>Highly configurable logging systems such as logback and log4j
may create components which invoke loggers during their own
initialization. See issue <a
href="http://jira.qos.ch/browse/LOGBACK-127">LOGBACK-127</a> for a
typical occurrence. However, since the binding process with SLF4J
has not yet completed (because the underlying logging system was
not yet completely loaded into memory), it is not possible to
<p> Highly configurable
logging systems such as logback and log4j may create components
which invoke loggers during their own initialization. See issue
<a href="http://jira.qos.ch/browse/LOGBACK-127">LOGBACK-127</a>
for a typical occurrence. However, since the binding process with
SLF4J has not yet completed (because the underlying logging system
was not yet completely loaded into memory), it is not possible to
honor such logger creation requests.</p>
<p>To avoid this chicken-and-egg problem, SLF4J creates substitute
@ -476,6 +476,23 @@ class B extends A {
have been dropped.
</p>
<h3 class="doAnchor" name="reply">A number (N) of logging calls
during the initialization phase have been intercepted and are now
being replayed. These are suject to the filtering rules of the
underlying logging system.
</h3>
<p><span class="label">since 1.7.15</span> As of SLF4J version
1.7.15, logging calls made during the initilization phase are
recorded and replayed post-inititilization.</p>
<p>These replayed logging calls are subject to filtering by the
underlying logging system.</p>
<p>Replaying only occurs for apllications which are already
multi-threaded when the first logging call occurs.
</p>
<script src="templates/footer.js" type="text/javascript"></script>
</div>