logback-dev
Threads by month
- ----- 2025 -----
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
November 2008
- 13 participants
- 135 discussions

[JIRA] Commented: (LBCORE-63) Deadlock when running on multiple core processors
by Paweł Paprota (JIRA) 15 Nov '08
by Paweł Paprota (JIRA) 15 Nov '08
15 Nov '08
[ http://jira.qos.ch/browse/LBCORE-63?page=com.atlassian.jira.plugin.system.i… ]
Paweł Paprota commented on LBCORE-63:
-------------------------------------
What is the current status of this issue? I think I got hit by this one, at least that's what I'm seeing right now in Eclipse when I suspend all threads in my application - one thread has callAppenders in its stack trace and it hanged (yesterday) on socketWrite. Some of the other threads seem to be waiting on a lock in callAppenders. As a result, I don't see any more logging appended to my log file and the console.
I'm using logback-core/classic 0.9.9 with slf4j 1.5.3.
Did the Ralph's patch make it to any of the newer logback releases? I was not able to find any change log or release announcement for releases newer than 0.9.9...
I would really appreciate some feedback, thank you.
Below is the stack trace of the "offending" thread (only relevant calls included):
Thread [Thread-12] (Suspended)
SocketOutputStream.socketWrite0(FileDescriptor, byte[], int, int) line: not available [native method]
SocketOutputStream.socketWrite(byte[], int, int) line: 92
SocketOutputStream.write(byte[], int, int) line: 136
ObjectOutputStream$BlockDataOutputStream.drain() line: 1685
ObjectOutputStream$BlockDataOutputStream.write(byte[], int, int, boolean) line: 1649
ObjectOutputStream.defaultWriteFields(Object, ObjectStreamClass) line: 1368
ObjectOutputStream.writeSerialData(Object, ObjectStreamClass) line: 1347
ObjectOutputStream.writeOrdinaryObject(Object, ObjectStreamClass, boolean) line: 1290
ObjectOutputStream.writeObject0(Object, boolean) line: 1079
ObjectOutputStream.writeArray(Object, ObjectStreamClass, boolean) line: 1251
ObjectOutputStream.writeObject0(Object, boolean) line: 1075
ObjectOutputStream.defaultWriteFields(Object, ObjectStreamClass) line: 1375
ObjectOutputStream.defaultWriteObject() line: 391
LoggingEvent.writeObject(ObjectOutputStream) line: 288
GeneratedMethodAccessor1118.invoke(Object, Object[]) line: not available
DelegatingMethodAccessorImpl.invoke(Object, Object[]) line: 25
Method.invoke(Object, Object...) line: 585
ObjectStreamClass.invokeWriteObject(Object, ObjectOutputStream) line: 917
ObjectOutputStream.writeSerialData(Object, ObjectStreamClass) line: 1339
ObjectOutputStream.writeOrdinaryObject(Object, ObjectStreamClass, boolean) line: 1290
ObjectOutputStream.writeObject0(Object, boolean) line: 1079
ObjectOutputStream.writeObject(Object) line: 302
SocketAppender(SocketAppenderBase<E>).append(E) line: 163
SocketAppender(AppenderBase<E>).doAppend(E) line: 81
AppenderAttachableImpl<E>.appendLoopOnAppenders(E) line: 51
Logger.appendLoopOnAppenders(LoggingEvent) line: 282
Logger.callAppenders(LoggingEvent) line: 266
Logger.buildLoggingEventAndAppend(String, Marker, Level, String, Object[], Throwable) line: 487
Logger.filterAndLog(String, Marker, Level, String, Object, Object, Throwable) line: 460
Logger.debug(String, Object, Object) line: 406
And those are the ones being blocked:
Thread [Thread-4] (Suspended)
Logger.callAppenders(LoggingEvent) line: 265
Logger.buildLoggingEventAndAppend(String, Marker, Level, String, Object[], Throwable) line: 487
Logger.filterAndLog(String, Marker, Level, String, Object[], Throwable) line: 479
Logger.debug(String, Throwable) line: 414
Daemon Thread [ActiveMQ Connection Worker: tcp://localhost/127.0.0.1:61616] (Suspended)
Logger.callAppenders(LoggingEvent) line: 265
Logger.buildLoggingEventAndAppend(String, Marker, Level, String, Object[], Throwable) line: 487
Logger.filterAndLog(String, Marker, Level, String, Object[], Throwable) line: 479
Logger.log(Marker, String, int, String, Throwable) line: 818
SLF4JLocationAwareLog.info(Object, Throwable) line: 152
AdvisoryConsumer.dispose() line: 58
ActiveMQConnection.close() line: 563
CachingConnectionFactory(SingleConnectionFactory).closeConnection(Connection) line: 364
CachingConnectionFactory(SingleConnectionFactory).resetConnection() line: 302
CachingConnectionFactory.resetConnection() line: 120
CachingConnectionFactory(SingleConnectionFactory).onException(JMSException) line: 283
SingleConnectionFactory$InternalChainedExceptionListener(ChainedExceptionListener).onException(JMSException) line: 60
ActiveMQConnection$3.run() line: 1690
ThreadPoolExecutor$Worker.runTask(Runnable) line: 650
ThreadPoolExecutor$Worker.run() line: 675
Thread.run() line: 595
> Deadlock when running on multiple core processors
> -------------------------------------------------
>
> Key: LBCORE-63
> URL: http://jira.qos.ch/browse/LBCORE-63
> Project: logback-core
> Issue Type: Sub-task
> Affects Versions: 0.9.9
> Environment: Operating System: Windows
> Platform: PC
> Reporter: Toni Heimala
> Assignee: Ceki Gulcu
> Priority: Blocker
> Attachments: patch.txt, patch2.txt, patch3.txt
>
>
> When you run logging into same file from many threads on a system that has more than one physical processor (Dual Core for example), a deadlock will occur after a while. This can not be reproduced on HyperThreading processors. Here's an example program that will demonstrate the behavior:
> -----------------------------
> Main.java
> -----------------------------
> import java.util.Date;
> import java.util.concurrent.ScheduledThreadPoolExecutor;
> import java.util.concurrent.TimeUnit;
> import org.slf4j.Logger;
> import org.slf4j.LoggerFactory;
> import ch.qos.logback.classic.LoggerContext;
> import ch.qos.logback.classic.joran.JoranConfigurator;
> import ch.qos.logback.core.joran.spi.JoranException;
> public class Main extends Thread
> {
> private final static String LOGGER_CONFIGURATION_FILE = "logger.xml";
> private final Logger logger = LoggerFactory.getLogger(Main.class);
>
> private final long start;
>
> public Main()
> throws JoranException
> {
> start = new Date().getTime();
> LoggerContext lc = (LoggerContext)LoggerFactory.getILoggerFactory();
> JoranConfigurator configurator = new JoranConfigurator();
> lc.shutdownAndReset();
> configurator.setContext(lc);
> configurator.doConfigure(LOGGER_CONFIGURATION_FILE);
> }
>
> public void start()
> {
> ScheduledThreadPoolExecutor ex1 = new ScheduledThreadPoolExecutor(1);
> ScheduledThreadPoolExecutor ex2 = new ScheduledThreadPoolExecutor(1);
> ScheduledThreadPoolExecutor ex3 = new ScheduledThreadPoolExecutor(1);
> ScheduledThreadPoolExecutor ex4 = new ScheduledThreadPoolExecutor(1);
> ScheduledThreadPoolExecutor ex5 = new ScheduledThreadPoolExecutor(1);
> ex1.scheduleAtFixedRate(new Task("EX1"), 10, 10, TimeUnit.MICROSECONDS);
> ex2.scheduleAtFixedRate(new Task("EX2"), 10, 10, TimeUnit.MICROSECONDS);
> ex3.scheduleAtFixedRate(new Task("EX3"), 10, 10, TimeUnit.MICROSECONDS);
> ex4.scheduleAtFixedRate(new Task("EX4"), 10, 10, TimeUnit.MICROSECONDS);
> ex5.scheduleAtFixedRate(new Task("EX5"), 10, 10, TimeUnit.MICROSECONDS);
>
> super.start();
> }
>
> public void run()
> {
> try
> {
> while(true)
> {
> logger.debug("[MAIN] {}", new Date().getTime() - start);
> Thread.sleep(10);
> }
> }
> catch (InterruptedException e)
> {
> logger.info("[MAIN]: Interrupted: {}", e.getMessage());
> }
> }
>
> public static void main(String[] args)
> {
> try
> {
> Main main = new Main();
> main.start();
> }
> catch (JoranException e)
> {
> System.out.println("Failed to load application: " + e.getMessage());
> }
> }
> }
> -------------------------------
> Task.java
> -------------------------------
> import java.util.Date;
> import org.slf4j.Logger;
> import org.slf4j.LoggerFactory;
> public class Task implements Runnable
> {
> private final Logger logger = LoggerFactory.getLogger(Task.class);
> private final Logger logger_main = LoggerFactory.getLogger(Main.class);
> private final String name;
> private final long start;
>
> public Task(final String name)
> {
> this.name = name;
> start = new Date().getTime();
> }
> public void run()
> {
> logger.debug("[{}] {}", name, new Date().getTime() - start);
> logger_main.debug("[MAIN] - [{}] {}", name, new Date().getTime() - start);
> }
> }
--
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators: http://jira.qos.ch/secure/Administrators.jspa
-
For more information on JIRA, see: http://www.atlassian.com/software/jira
1
0

14 Nov '08
Author: ceki
Date: Fri Nov 14 15:21:57 2008
New Revision: 1972
Added:
logback/trunk/logback-classic/src/test/generateLogsInSafeMode.sh
- copied, changed from r1971, /logback/trunk/logback-classic/src/test/gerateLogFileInSafeMode.sh
Removed:
logback/trunk/logback-classic/src/test/gerateLogFileInSafeMode.sh
Modified:
logback/trunk/logback-classic/src/test/allInOneSafeMode.sh
logback/trunk/logback-classic/src/test/checkResults.sh
Log:
ongoing work
Modified: logback/trunk/logback-classic/src/test/allInOneSafeMode.sh
==============================================================================
--- logback/trunk/logback-classic/src/test/allInOneSafeMode.sh (original)
+++ logback/trunk/logback-classic/src/test/allInOneSafeMode.sh Fri Nov 14 15:21:57 2008
@@ -1,8 +1,12 @@
+# Use this script both generate and check the results. It only works
+# if there is only one instance of this script
+
echo "File name $1"
echo "run length $2"
+
#On windows
#CLASSPATH="${CLASSPATH};./target/classes/"
#CLASSPATH="${CLASSPATH};./target/test-classes/"
@@ -15,11 +19,11 @@
#CLASSPATH="${CLASSPATH}:../logback-core/target/classes"
#CLASSPATH="${CLASSPATH}:../logback-examples/lib/slf4j-api-1.5.5.jar"
-#if [ -f $1 ]
-#then
-# echo Removing $1 before tests
-# rm $1;
-#fi
+if [ -f $1 ]
+then
+ echo Removing $1 before tests
+ rm $1;
+fi
if [ $# -lt 3 ]
then
Modified: logback/trunk/logback-classic/src/test/checkResults.sh
==============================================================================
--- logback/trunk/logback-classic/src/test/checkResults.sh (original)
+++ logback/trunk/logback-classic/src/test/checkResults.sh Fri Nov 14 15:21:57 2008
@@ -1,3 +1,7 @@
+# Use this script to check the output generated by
+# the generateLogsInSafeMode.sh script
+# Note that you can pass multiple stamps
+
echo "File name $1"
echo "run length $2"
Copied: logback/trunk/logback-classic/src/test/generateLogsInSafeMode.sh (from r1971, /logback/trunk/logback-classic/src/test/gerateLogFileInSafeMode.sh)
==============================================================================
--- /logback/trunk/logback-classic/src/test/gerateLogFileInSafeMode.sh (original)
+++ logback/trunk/logback-classic/src/test/generateLogsInSafeMode.sh Fri Nov 14 15:21:57 2008
@@ -15,15 +15,10 @@
#CLASSPATH="${CLASSPATH}:../logback-core/target/classes"
#CLASSPATH="${CLASSPATH}:../logback-examples/lib/slf4j-api-1.5.5.jar"
-#if [ -f $1 ]
-#then
-# echo Removing $1 before tests
-# rm $1;
-#fi
if [ $# -lt 3 ]
then
- echo "Usage: generateInSafeMode.sh filename runLen stamp0 ... stampN"
+ echo "Usage: generateLogsInSafeMode.sh filename runLen stamp0 ... stampN"
exit 1;
fi
1
0

14 Nov '08
Author: ceki
Date: Fri Nov 14 15:15:42 2008
New Revision: 1971
Added:
logback/trunk/logback-classic/src/test/allInOneSafeMode.sh
- copied unchanged from r1970, /logback/trunk/logback-classic/src/test/testSafeMode.sh
logback/trunk/logback-classic/src/test/checkResults.sh
logback/trunk/logback-classic/src/test/gerateLogFileInSafeMode.sh
Removed:
logback/trunk/logback-classic/src/test/testSafeMode.sh
Log:
- ongoing work
Added: logback/trunk/logback-classic/src/test/checkResults.sh
==============================================================================
--- (empty file)
+++ logback/trunk/logback-classic/src/test/checkResults.sh Fri Nov 14 15:15:42 2008
@@ -0,0 +1,16 @@
+echo "File name $1"
+echo "run length $2"
+
+if [ $# -lt 3 ]
+then
+ echo "Usage: checkResults.sh filename runLen stamp0 ... stampN"
+ exit 1;
+fi
+
+FILENAME=$1
+LEN=$2
+
+shift 2
+
+echo Checking results...
+java ch.qos.logback.classic.multiJVM.Checker $LEN $FILENAME $*
Added: logback/trunk/logback-classic/src/test/gerateLogFileInSafeMode.sh
==============================================================================
--- (empty file)
+++ logback/trunk/logback-classic/src/test/gerateLogFileInSafeMode.sh Fri Nov 14 15:15:42 2008
@@ -0,0 +1,45 @@
+echo "File name $1"
+echo "run length $2"
+
+
+
+#On windows
+#CLASSPATH="${CLASSPATH};./target/classes/"
+#CLASSPATH="${CLASSPATH};./target/test-classes/"
+#CLASSPATH="${CLASSPATH};../logback-core/target/classes"
+#CLASSPATH="${CLASSPATH};../logback-examples/lib/slf4j-api-1.5.5.jar"
+
+# On Unix
+#CLASSPATH="${CLASSPATH}:./target/classes/"
+#CLASSPATH="${CLASSPATH}:./target/test-classes/"
+#CLASSPATH="${CLASSPATH}:../logback-core/target/classes"
+#CLASSPATH="${CLASSPATH}:../logback-examples/lib/slf4j-api-1.5.5.jar"
+
+#if [ -f $1 ]
+#then
+# echo Removing $1 before tests
+# rm $1;
+#fi
+
+if [ $# -lt 3 ]
+then
+ echo "Usage: generateInSafeMode.sh filename runLen stamp0 ... stampN"
+ exit 1;
+fi
+
+FILENAME=$1
+LEN=$2
+
+shift 2
+
+for stamp in $@
+do
+ echo running safe mode with $stamp
+ java ch.qos.logback.classic.multiJVM.SafeModeFileAppender $stamp $LEN $FILENAME &
+done
+
+wait
+
+echo "To test the results issue the following command"
+echo "./checkResults.sh $FILENAME $LEN $*"
+
1
0

14 Nov '08
Author: ceki
Date: Fri Nov 14 11:52:06 2008
New Revision: 1970
Modified:
logback/trunk/logback-classic/src/test/testSafeMode.sh
Log:
- we have to manually delete the log file when testing
from multiple JVM on different hosts
Modified: logback/trunk/logback-classic/src/test/testSafeMode.sh
==============================================================================
--- logback/trunk/logback-classic/src/test/testSafeMode.sh (original)
+++ logback/trunk/logback-classic/src/test/testSafeMode.sh Fri Nov 14 11:52:06 2008
@@ -15,11 +15,11 @@
#CLASSPATH="${CLASSPATH}:../logback-core/target/classes"
#CLASSPATH="${CLASSPATH}:../logback-examples/lib/slf4j-api-1.5.5.jar"
-if [ -f $1 ]
-then
- echo Removing $1 before tests
- rm $1;
-fi
+#if [ -f $1 ]
+#then
+# echo Removing $1 before tests
+# rm $1;
+#fi
if [ $# -lt 3 ]
then
1
0

svn commit: r1968 - in logback/trunk: logback-classic/src/test/java/ch/qos/logback/classic/multiJVM logback-core/src/main/java/ch/qos/logback/core logback-examples/src/main/java/chapter4
by noreply.ceki@qos.ch 13 Nov '08
by noreply.ceki@qos.ch 13 Nov '08
13 Nov '08
Author: ceki
Date: Thu Nov 13 18:54:51 2008
New Revision: 1968
Added:
logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/multiJVM/
logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/multiJVM/Checker.java
logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/multiJVM/LoggingThread.java
logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/multiJVM/SafeModeFileAppender.java
Modified:
logback/trunk/logback-core/src/main/java/ch/qos/logback/core/FileAppender.java
logback/trunk/logback-core/src/main/java/ch/qos/logback/core/WriterAppender.java
logback/trunk/logback-examples/src/main/java/chapter4/IO.java
Log:
LBCORE-29
Experimental support for multiple JVM's writing to the same file
Added: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/multiJVM/Checker.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/multiJVM/Checker.java Thu Nov 13 18:54:51 2008
@@ -0,0 +1,89 @@
+/**
+ * Logback: the generic, reliable, fast and flexible logging framework.
+ *
+ * Copyright (C) 2000-2008, QOS.ch
+ *
+ * This library is free software, you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation.
+ */
+
+package ch.qos.logback.classic.multiJVM;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class Checker {
+
+ static long LEN;
+ static String FILENAME;
+
+ static void usage(String msg) {
+ System.err.println(msg);
+ System.err
+ .println("Usage: java "
+ + Checker.class.getName()
+ + " runLength filename stamp0 stamp1 ..\n"
+ + " runLength (integer) the number of logs to generate perthread\n"
+ + " filename (string) the filename where to write\n"
+ + " stamp0 JVM instance stamp0\n"
+ + " stamp1 JVM instance stamp1\n");
+ System.exit(1);
+ }
+
+ public static void main(String[] argv) throws Exception {
+ if (argv.length < 3) {
+ usage("Wrong number of arguments.");
+ }
+
+ LEN = Integer.parseInt(argv[0]);
+ FILENAME = argv[1];
+
+ for (int i = 2; i < argv.length; i++) {
+ check(argv[i], FILENAME, true);
+ }
+ }
+
+ static void check(String stamp, String filename, boolean safetyMode)
+ throws Exception {
+
+ FileReader fr = new FileReader(FILENAME);
+ BufferedReader br = new BufferedReader(fr);
+
+ String regExp = "^" + stamp + " DEBUG - " + LoggingThread.msgLong
+ + " (\\d+)$";
+ // System.out.println(regExp);
+ Pattern p = Pattern.compile(regExp);
+
+ String line;
+ int expected = 0;
+ while ((line = br.readLine()) != null) {
+ // System.out.println(line);
+ Matcher m = p.matcher(line);
+ if (m.matches()) {
+ String g = m.group(1);
+ int num = Integer.parseInt(g);
+ if (num != expected) {
+ System.err.println("ERROR: out of sequence line: ");
+ System.err.println(line);
+ return;
+ }
+ expected++;
+ }
+ }
+
+ if (expected != LEN) {
+ System.err.println("ERROR: For JVM stamp " + stamp + " found " + expected
+ + " was expecting " + LEN);
+ } else {
+ System.out.println("For JVM stamp " + stamp + " found " + LEN
+ + " lines in correct sequence");
+ }
+ fr.close();
+ br.close();
+
+ }
+
+}
Added: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/multiJVM/LoggingThread.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/multiJVM/LoggingThread.java Thu Nov 13 18:54:51 2008
@@ -0,0 +1,45 @@
+/**
+ * Logback: the generic, reliable, fast and flexible logging framework.
+ *
+ * Copyright (C) 2000-2008, QOS.ch
+ *
+ * This library is free software, you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation.
+ */
+
+package ch.qos.logback.classic.multiJVM;
+
+import org.slf4j.Logger;
+
+public class LoggingThread extends Thread {
+ static String msgLong = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
+
+ final long len;
+ final Logger logger;
+ private double durationPerLog;
+
+ public LoggingThread(Logger logger, long len) {
+ this.logger = logger;
+ this.len = len;
+ }
+
+ public void run() {
+ long before = System.nanoTime();
+ for (int i = 0; i < len; i++) {
+ logger.debug(msgLong + " " + i);
+// try {
+// Thread.sleep(100);
+// } catch (InterruptedException e) {
+// }
+ }
+ // in microseconds
+ durationPerLog = (System.nanoTime() - before) / (len * 1000.0);
+ }
+
+ public double getDurationPerLogInMicroseconds() {
+ return durationPerLog;
+ }
+
+
+}
Added: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/multiJVM/SafeModeFileAppender.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/multiJVM/SafeModeFileAppender.java Thu Nov 13 18:54:51 2008
@@ -0,0 +1,95 @@
+/**
+ * Logback: the generic, reliable, fast and flexible logging framework.
+ *
+ * Copyright (C) 2000-2008, QOS.ch
+ *
+ * This library is free software, you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation.
+ */
+
+package ch.qos.logback.classic.multiJVM;
+
+import org.slf4j.Logger;
+
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.PatternLayout;
+import ch.qos.logback.classic.spi.LoggingEvent;
+import ch.qos.logback.core.FileAppender;
+
+/**
+ * An application to write to a file using a FileAppender in safe mode.
+ *
+ * @author Ceki Gulcu
+ *
+ */
+public class SafeModeFileAppender {
+
+ static long LEN;
+ static String FILENAME;
+ static String STAMP;
+
+ static public void main(String[] argv) throws Exception {
+ if (argv.length != 3) {
+ usage("Wrong number of arguments.");
+ }
+
+ STAMP = argv[0];
+ LEN = Integer.parseInt(argv[1]);
+ FILENAME = argv[2];
+ writeContinously(STAMP, FILENAME, true);
+ }
+
+ static void usage(String msg) {
+ System.err.println(msg);
+ System.err.println("Usage: java " + SafeModeFileAppender.class.getName()
+ + " stamp runLength filename\n" + " stamp JVM instance stamp\n"
+ + " runLength (integer) the number of logs to generate perthread"
+ + " filename (string) the filename where to write\n");
+ System.exit(1);
+ }
+
+ static LoggerContext buildLoggerContext(String stamp, String filename,
+ boolean safetyMode) {
+ LoggerContext loggerContext = new LoggerContext();
+
+ FileAppender<LoggingEvent> fa = new FileAppender<LoggingEvent>();
+
+ PatternLayout patternLayout = new PatternLayout();
+ patternLayout.setPattern(stamp + " %5p - %m%n");
+ patternLayout.setContext(loggerContext);
+ patternLayout.start();
+
+ fa.setLayout(patternLayout);
+ fa.setFile(filename);
+ fa.setAppend(true);
+ fa.setImmediateFlush(true);
+ fa.setBufferedIO(false);
+ fa.setSafeMode(safetyMode);
+ fa.setContext(loggerContext);
+ fa.start();
+
+ ch.qos.logback.classic.Logger root = loggerContext
+ .getLogger(LoggerContext.ROOT_NAME);
+ root.addAppender(fa);
+
+ return loggerContext;
+ }
+
+ static void writeContinously(String stamp, String filename, boolean safetyMode)
+ throws Exception {
+ LoggerContext lc = buildLoggerContext(stamp, filename, safetyMode);
+ Logger logger = lc.getLogger(SafeModeFileAppender.class);
+
+ long before = System.nanoTime();
+ for (int i = 0; i < LEN; i++) {
+ logger.debug(LoggingThread.msgLong + " " + i);
+ }
+ lc.stop();
+ double durationPerLog = (System.nanoTime() - before) / (LEN * 1000.0);
+
+ System.out.println("Average duration of " + (durationPerLog)
+ + " microseconds per log. Safety mode " + safetyMode);
+ System.out.println("------------------------------------------------");
+ }
+}
Modified: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/FileAppender.java
==============================================================================
--- logback/trunk/logback-core/src/main/java/ch/qos/logback/core/FileAppender.java (original)
+++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/FileAppender.java Thu Nov 13 18:54:51 2008
@@ -14,6 +14,8 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Writer;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
import ch.qos.logback.core.status.ErrorStatus;
import ch.qos.logback.core.status.InfoStatus;
@@ -51,6 +53,10 @@
*/
protected int bufferSize = 8 * 1024;
+ private boolean safeMode = false;
+
+ private FileChannel fileChannel = null;
+
/**
* As in most cases, the default constructor does nothing.
*/
@@ -149,7 +155,11 @@
}
}
- Writer w = createWriter(new FileOutputStream(fileName, append));
+ FileOutputStream fileOutputStream = new FileOutputStream(fileName, append);
+ if (safeMode) {
+ fileChannel = fileOutputStream.getChannel();
+ }
+ Writer w = createWriter(fileOutputStream);
if (bufferedIO) {
w = new BufferedWriter(w, bufferSize);
}
@@ -172,7 +182,48 @@
this.bufferSize = bufferSize;
}
+ public String getFileName() {
+ return fileName;
+ }
+
+ public void setFileName(String fileName) {
+ this.fileName = fileName;
+ }
+
+ public boolean isSafeMode() {
+ return safeMode;
+ }
+
+ public void setSafeMode(boolean safeMode) {
+ this.safeMode = safeMode;
+ }
+
public void setAppend(boolean append) {
this.append = append;
}
+
+ @Override
+ protected void writerWrite(String s, boolean flush) throws IOException {
+ if (safeMode && fileChannel != null) {
+ FileLock fileLock = null;
+ try {
+ fileLock = fileChannel.lock();
+ long position = fileChannel.position();
+ long size = fileChannel.size();
+ if(size != position) {
+ //System.out.println("position size mismatch, pos ="+position+" size="+size);
+ fileChannel.position(size);
+ } else {
+ //System.out.println(position+" size="+size);
+ }
+ super.writerWrite(s, true);
+ } finally {
+ if (fileLock != null) {
+ fileLock.release();
+ }
+ }
+ } else {
+ super.writerWrite(s, flush);
+ }
+ }
}
Modified: logback/trunk/logback-core/src/main/java/ch/qos/logback/core/WriterAppender.java
==============================================================================
--- logback/trunk/logback-core/src/main/java/ch/qos/logback/core/WriterAppender.java (original)
+++ logback/trunk/logback-core/src/main/java/ch/qos/logback/core/WriterAppender.java Thu Nov 13 18:54:51 2008
@@ -36,16 +36,14 @@
* is a good chance that the last few logs events are not actually written to
* persistent media if and when the application crashes.
*
- * <p>
- * The <code>immediateFlush</code> variable is set to <code>true</code> by
- * default.
+ * <p> The <code>immediateFlush</code> variable is set to <code>true</code>
+ * by default.
*/
private boolean immediateFlush = true;
/**
- * The encoding to use when opening an InputStream.
- * <p>
- * The <code>encoding</code> variable is set to <code>null</null> by default
+ * The encoding to use when opening an InputStream. <p> The
+ * <code>encoding</code> variable is set to <code>null</null> by default
* which results in the use of the system's default encoding.
*/
private String encoding;
@@ -71,14 +69,12 @@
* If the <b>ImmediateFlush</b> option is set to <code>true</code>, the
* appender will flush at the end of each write. This is the default behavior.
* If the option is set to <code>false</code>, then the underlying stream
- * can defer writing to physical medium to a later time.
- * <p>
- * Avoiding the flush operation at the end of each append results in a
- * performance gain of 10 to 20 percent. However, there is safety tradeoff
- * involved in skipping flushing. Indeed, when flushing is skipped, then it is
- * likely that the last few log events will not be recorded on disk when the
- * application exits. This is a high price to pay even for a 20% performance
- * gain.
+ * can defer writing to physical medium to a later time. <p> Avoiding the
+ * flush operation at the end of each append results in a performance gain of
+ * 10 to 20 percent. However, there is safety tradeoff involved in skipping
+ * flushing. Indeed, when flushing is skipped, then it is likely that the last
+ * few log events will not be recorded on disk when the application exits.
+ * This is a high price to pay even for a 20% performance gain.
*/
public void setImmediateFlush(boolean value) {
immediateFlush = value;
@@ -127,8 +123,7 @@
* Stop this appender instance. The underlying stream or writer is also
* closed.
*
- * <p>
- * Stopped appenders cannot be reused.
+ * <p> Stopped appenders cannot be reused.
*/
public synchronized void stop() {
closeWriter();
@@ -205,21 +200,17 @@
void writeHeader() {
if (layout != null && (this.writer != null)) {
try {
-
- String h = layout.getFileHeader();
- if (h != null) {
- this.writer.write(h);
- }
- String ph = layout.getPresentationHeader();
- if (ph != null) {
- this.writer.write(ph);
- }
- // If at least one of file header or presentation header were not null, then append a line separator.
- // This should be useful in most cases and should not hurt.
- if ((h != null) || (ph != null)) {
- this.writer.write(Layout.LINE_SEP);
- this.writer.flush();
+ StringBuilder sb = new StringBuilder();
+ appendIfNotNull(sb, layout.getFileHeader());
+ appendIfNotNull(sb, layout.getPresentationHeader());
+ if (sb.length() > 0) {
+ sb.append(Layout.LINE_SEP);
+ // If at least one of file header or presentation header were not
+ // null, then append a line separator.
+ // This should be useful in most cases and should not hurt.
+ writerWrite(sb.toString(), true);
}
+
} catch (IOException ioe) {
this.started = false;
addStatus(new ErrorStatus("Failed to write header for appender named ["
@@ -228,19 +219,21 @@
}
}
+ private void appendIfNotNull(StringBuilder sb, String s) {
+ if (s != null) {
+ sb.append(s);
+ }
+ }
+
void writeFooter() {
if (layout != null && this.writer != null) {
try {
- String pf = layout.getPresentationFooter();
- if (pf != null) {
- this.writer.write(pf);
- }
- String h = layout.getFileFooter();
- if (h != null) {
- this.writer.write(h);
+ StringBuilder sb = new StringBuilder();
+ appendIfNotNull(sb, layout.getPresentationFooter());
+ appendIfNotNull(sb, layout.getFileFooter());
+ if (sb.length() > 0) {
+ writerWrite(sb.toString(), true); // force flush
}
- // flushing is mandatory if the writer is not later closed.
- this.writer.flush();
} catch (IOException ioe) {
this.started = false;
addStatus(new ErrorStatus("Failed to write footer for appender named ["
@@ -250,10 +243,9 @@
}
/**
- * <p>
- * Sets the Writer where the log output will go. The specified Writer must be
- * opened by the user and be writable. The <code>java.io.Writer</code> will
- * be closed when the appender instance is closed.
+ * <p> Sets the Writer where the log output will go. The specified Writer must
+ * be opened by the user and be writable. The <code>java.io.Writer</code>
+ * will be closed when the appender instance is closed.
*
* @param writer
* An already opened Writer.
@@ -266,11 +258,16 @@
writeHeader();
}
+ protected void writerWrite(String s, boolean flush) throws IOException {
+ this.writer.write(s);
+ if (flush) {
+ this.writer.flush();
+ }
+ }
+
/**
- * Actual writing occurs here.
- * <p>
- * Most subclasses of <code>WriterAppender</code> will need to override this
- * method.
+ * Actual writing occurs here. <p> Most subclasses of
+ * <code>WriterAppender</code> will need to override this method.
*
* @since 0.9.0
*/
@@ -280,10 +277,7 @@
}
try {
- this.writer.write(this.layout.doLayout(event));
- if (this.immediateFlush) {
- this.writer.flush();
- }
+ writerWrite(this.layout.doLayout(event), this.immediateFlush);
} catch (IOException ioe) {
// as soon as an exception occurs, move to non-started state
// and add a single ErrorStatus to the SM.
Modified: logback/trunk/logback-examples/src/main/java/chapter4/IO.java
==============================================================================
--- logback/trunk/logback-examples/src/main/java/chapter4/IO.java (original)
+++ logback/trunk/logback-examples/src/main/java/chapter4/IO.java Thu Nov 13 18:54:51 2008
@@ -1,7 +1,7 @@
/**
- * Logback: the reliable, generic, fast and flexible logging framework.
+ * Logback: the generic, reliable, fast and flexible logging framework.
*
- * Copyright (C) 1999-2006, QOS.ch
+ * Copyright (C) 2000-2008, QOS.ch
*
* This library is free software, you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
@@ -117,7 +117,7 @@
threads[i].start();
}
- // wait for them to stop, compute the average throughputs
+ // wait for them to stop, compute the average throughput
double sum = 0;
for (int i = 0; i < numThreads; i++) {
@@ -131,8 +131,8 @@
counterThread.join();
}
- System.out.println("On average throughput of " + (sum / numThreads) +
- " logs per millisecond.");
+ System.out.println("On average throughput of " + (sum / numThreads)*1000 +
+ " logs per microsecond.");
System.out.println("------------------------------------------------");
}
@@ -143,16 +143,16 @@
msg = msgLong;
}
- long before = System.currentTimeMillis();
+ long before = System.nanoTime();
for (int i = 0; i < len; i++) {
logger.debug(msg);
}
- throughput = (len * 1.0) / (System.currentTimeMillis() - before);
+ throughput = (len * 1.0) / (System.nanoTime() - before);
System.out.println(getName() + ", buffered: " + buffered +
", immediateFlush: " + immediateFlush + ", throughput: " + throughput +
- " logs per millisecond.");
+ " logs per nanosecond.");
}
}
@@ -162,14 +162,14 @@
public double counter = 0;
public void run() {
- long before = System.currentTimeMillis();
+ long before = System.nanoTime();
while (!interrupted) {
- counter += 0.001;
+ counter += 1.0;
}
- double tput = (counter * 1.0) / (System.currentTimeMillis() - before);
+ double tput = (counter * 1.0) / (System.nanoTime() - before);
System.out.println("Counter thread " + getName() +
- " incremented counter by " + tput + " per millisecond.");
+ " incremented counter by " + tput + " per nanosecond.");
}
}
2
1

13 Nov '08
Author: ceki
Date: Thu Nov 13 18:57:21 2008
New Revision: 1969
Added:
logback/trunk/logback-classic/src/test/testSafeMode.sh
Log:
LBCORE-29
Experimental support for multiple JVM's writing to the same file
Added: logback/trunk/logback-classic/src/test/testSafeMode.sh
==============================================================================
--- (empty file)
+++ logback/trunk/logback-classic/src/test/testSafeMode.sh Thu Nov 13 18:57:21 2008
@@ -0,0 +1,44 @@
+echo "File name $1"
+echo "run length $2"
+
+
+
+#On windows
+#CLASSPATH="${CLASSPATH};./target/classes/"
+#CLASSPATH="${CLASSPATH};./target/test-classes/"
+#CLASSPATH="${CLASSPATH};../logback-core/target/classes"
+#CLASSPATH="${CLASSPATH};../logback-examples/lib/slf4j-api-1.5.5.jar"
+
+# On Unix
+#CLASSPATH="${CLASSPATH}:./target/classes/"
+#CLASSPATH="${CLASSPATH}:./target/test-classes/"
+#CLASSPATH="${CLASSPATH}:../logback-core/target/classes"
+#CLASSPATH="${CLASSPATH}:../logback-examples/lib/slf4j-api-1.5.5.jar"
+
+if [ -f $1 ]
+then
+ echo Removing $1 before tests
+ rm $1;
+fi
+
+if [ $# -lt 3 ]
+then
+ echo "Usage: testSafeMode.sh filename runLen stamp0 ... stampN"
+ exit 1;
+fi
+
+FILENAME=$1
+LEN=$2
+
+shift 2
+
+for stamp in $@
+do
+ echo running safe mode with $stamp
+ java ch.qos.logback.classic.multiJVM.SafeModeFileAppender $stamp $LEN $FILENAME &
+done
+
+wait
+
+echo Checking results...
+java ch.qos.logback.classic.multiJVM.Checker $LEN $FILENAME $*
1
0

svn commit: r1967 - in logback/trunk: logback-classic/src/main/java/ch/qos/logback/classic/pattern logback-classic/src/test/java/ch/qos/logback/classic/pattern logback-site/src/site/pages logback-site/src/site/pages/css logback-site/src/site/pages/manual
by noreply.ceki@qos.ch 12 Nov '08
by noreply.ceki@qos.ch 12 Nov '08
12 Nov '08
Author: ceki
Date: Wed Nov 12 19:40:35 2008
New Revision: 1967
Added:
logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ClassNameOnlyAbbreviator.java
logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/TargetLengthBasedClassNameAbbreviator.java
- copied, changed from r1899, /logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ClassNameAbbreviator.java
logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/TargetLengthBasedClassNameAbbreviatorTest.java
- copied, changed from r1899, /logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/ClassNameAbbreviatorTest.java
Removed:
logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ClassNameAbbreviator.java
logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/ClassNameAbbreviatorTest.java
Modified:
logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/NamedConverter.java
logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/ConverterTest.java
logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/PackageTest.java
logback/trunk/logback-site/src/site/pages/css/common.css
logback/trunk/logback-site/src/site/pages/manual/layouts.html
logback/trunk/logback-site/src/site/pages/news.html
Log:
The logger and class name converters now consider zero as
having special meaning, and will return the simple class name,
removing the package name prefix. This feature was asked by
Silvano Maffeis.
Added: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ClassNameOnlyAbbreviator.java
==============================================================================
--- (empty file)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ClassNameOnlyAbbreviator.java Wed Nov 12 19:40:35 2008
@@ -0,0 +1,30 @@
+/**
+ * Logback: the generic, reliable, fast and flexible logging framework.
+ *
+ * Copyright (C) 2000-2008, QOS.ch
+ *
+ * This library is free software, you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation.
+ */
+package ch.qos.logback.classic.pattern;
+
+import ch.qos.logback.classic.ClassicGlobal;
+
+/**
+ * This abbreviator returns the class name from a fully qualified class name,
+ * removing the leading package name.
+ *
+ * @author Ceki Gülcü
+ */
+public class ClassNameOnlyAbbreviator implements Abbreviator {
+
+ public String abbreviate(String fqClassName) {
+ int lastIndex = fqClassName.lastIndexOf(ClassicGlobal.DOT);
+ if (lastIndex != -1) {
+ return fqClassName.substring(lastIndex + 1, fqClassName.length());
+ } else {
+ return fqClassName;
+ }
+ }
+}
Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/NamedConverter.java
==============================================================================
--- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/NamedConverter.java (original)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/NamedConverter.java Wed Nov 12 19:40:35 2008
@@ -29,8 +29,10 @@
if (optStr != null) {
try {
int targetLen = Integer.parseInt(optStr);
- if (targetLen > 0) {
- abbreviator = new ClassNameAbbreviator(targetLen);
+ if (targetLen == 0) {
+ abbreviator = new ClassNameOnlyAbbreviator();
+ } else if (targetLen > 0) {
+ abbreviator = new TargetLengthBasedClassNameAbbreviator(targetLen);
}
} catch (NumberFormatException nfe) {
// FIXME: better error reporting
Copied: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/TargetLengthBasedClassNameAbbreviator.java (from r1899, /logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ClassNameAbbreviator.java)
==============================================================================
--- /logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ClassNameAbbreviator.java (original)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/pattern/TargetLengthBasedClassNameAbbreviator.java Wed Nov 12 19:40:35 2008
@@ -11,7 +11,7 @@
import ch.qos.logback.classic.ClassicGlobal;
-public class ClassNameAbbreviator implements Abbreviator {
+public class TargetLengthBasedClassNameAbbreviator implements Abbreviator {
static private final int BUF_LIMIT = 256;
static private final int MAX_DOTS = 12;
@@ -19,19 +19,19 @@
final int targetLength;
StringBuffer buf;
- public ClassNameAbbreviator(int targetLength) {
+ public TargetLengthBasedClassNameAbbreviator(int targetLength) {
this.targetLength = targetLength;
buf = new StringBuffer(targetLength);
}
- public String abbreviate(String className) {
- if (className == null) {
+ public String abbreviate(String fqClassName) {
+ if (fqClassName == null) {
throw new IllegalArgumentException("Class name may not be null");
}
- int inLen = className.length();
+ int inLen = fqClassName.length();
if (inLen < targetLength) {
- return className;
+ return fqClassName;
}
if (buf.capacity() > BUF_LIMIT) {
@@ -42,30 +42,31 @@
int[] dotArray = new int[MAX_DOTS];
int[] lengthArray = new int[MAX_DOTS];
- int dotCount = computeIndexes(className, dotArray);
+ int dotCount = computeIndexes(fqClassName, dotArray);
- //System.out.println();
- //System.out.println("Dot count for [" + className + "] is " + dotCount);
+ // System.out.println();
+ // System.out.println("Dot count for [" + className + "] is " + dotCount);
// if there are not dots than abbreviation is not possible
if (dotCount == 0) {
- return className;
+ return fqClassName;
}
- //printArray("dotArray: ", dotArray);
- computeLengthArray(className, dotArray, lengthArray, dotCount);
- //printArray("lengthArray: ", lengthArray);
+ // printArray("dotArray: ", dotArray);
+ computeLengthArray(fqClassName, dotArray, lengthArray, dotCount);
+ // printArray("lengthArray: ", lengthArray);
for (int i = 0; i <= dotCount; i++) {
if (i == 0) {
- buf.append(className.substring(0, lengthArray[i] - 1));
+ buf.append(fqClassName.substring(0, lengthArray[i] - 1));
} else {
- buf.append(className.substring(dotArray[i - 1], dotArray[i - 1]
+ buf.append(fqClassName.substring(dotArray[i - 1], dotArray[i - 1]
+ lengthArray[i]));
}
- //System.out.println("i=" + i + ", buf=" + buf);
+ // System.out.println("i=" + i + ", buf=" + buf);
}
return buf.toString();
}
+
static int computeIndexes(final String className, int[] dotArray) {
int dotCount = 0;
int k = 0;
@@ -85,9 +86,9 @@
void computeLengthArray(final String className, int[] dotArray,
int[] lengthArray, int dotCount) {
int toTrim = className.length() - targetLength;
- //System.out.println("toTrim=" + toTrim);
-
- //int toTrimAvarage = 0;
+ // System.out.println("toTrim=" + toTrim);
+
+ // int toTrimAvarage = 0;
int len;
for (int i = 0; i < dotCount; i++) {
@@ -99,7 +100,7 @@
// System.out.println("i=" + i + ", available = " + available);
len = (available < 1) ? available : 1;
- //System.out.println("i=" + i + ", toTrim = " + toTrim);
+ // System.out.println("i=" + i + ", toTrim = " + toTrim);
if (toTrim > 0) {
len = (available < 1) ? available : 1;
Modified: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/ConverterTest.java
==============================================================================
--- logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/ConverterTest.java (original)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/ConverterTest.java Wed Nov 12 19:40:35 2008
@@ -9,11 +9,13 @@
*/
package ch.qos.logback.classic.pattern;
+import static org.junit.Assert.*;
+
import java.util.ArrayList;
import java.util.List;
-import junit.framework.TestCase;
-
+import org.junit.Before;
+import org.junit.Test;
import org.slf4j.MDC;
import org.slf4j.MarkerFactory;
@@ -27,29 +29,16 @@
import ch.qos.logback.core.pattern.DynamicConverter;
import ch.qos.logback.core.pattern.FormatInfo;
-public class ConverterTest extends TestCase {
+public class ConverterTest {
LoggerContext lc = new LoggerContext();
Logger logger = lc.getLogger(ConverterTest.class);
LoggingEvent le;
List<String> optionList = new ArrayList<String>();
- public ConverterTest(String arg0) {
- super(arg0);
-
- Exception rootEx = getException("Innermost", null);
- Exception nestedEx = getException("Nested", rootEx);
-
- Exception ex = new Exception("Bogus exception", nestedEx);
-
- le = makeLoggingEvent(ex);
- // ex.printStackTrace();
- }
-
// The LoggingEvent is massaged with an FCQN of FormattingConverter. This
- // forces the
- // returned caller information to match the caller stack for this this
- // particular test.
+ // forces the returned caller information to match the caller stack for this
+ // this particular test.
LoggingEvent makeLoggingEvent(Exception ex) {
return new LoggingEvent(
ch.qos.logback.core.pattern.FormattingConverter.class.getName(),
@@ -60,24 +49,28 @@
return new Exception(msg, cause);
}
- protected void setUp() throws Exception {
- super.setUp();
- }
+ @Before
+ public void setUp() throws Exception {
+ Exception rootEx = getException("Innermost", null);
+ Exception nestedEx = getException("Nested", rootEx);
- protected void tearDown() throws Exception {
- super.tearDown();
+ Exception ex = new Exception("Bogus exception", nestedEx);
+
+ le = makeLoggingEvent(ex);
}
+ @Test
public void testLineOfCaller() {
{
DynamicConverter<LoggingEvent> converter = new LineOfCallerConverter();
StringBuffer buf = new StringBuffer();
converter.write(buf, le);
// the number below should be the line number of the previous line
- assertEquals("75", buf.toString());
+ assertEquals("67", buf.toString());
}
}
+ @Test
public void testLevel() {
{
DynamicConverter<LoggingEvent> converter = new LevelConverter();
@@ -94,6 +87,7 @@
}
}
+ @Test
public void testThread() {
DynamicConverter<LoggingEvent> converter = new ThreadConverter();
StringBuffer buf = new StringBuffer();
@@ -102,6 +96,7 @@
assertTrue(buf.toString().matches(regex));
}
+ @Test
public void testMessage() {
DynamicConverter<LoggingEvent> converter = new MessageConverter();
StringBuffer buf = new StringBuffer();
@@ -109,6 +104,7 @@
assertEquals("Some message", buf.toString());
}
+ @Test
public void testLineSeparator() {
DynamicConverter<LoggingEvent> converter = new LineSeparatorConverter();
StringBuffer buf = new StringBuffer();
@@ -116,12 +112,12 @@
assertEquals(CoreConstants.LINE_SEPARATOR, buf.toString());
}
+ @Test
public void testException() {
{
DynamicConverter<LoggingEvent> converter = new ThrowableProxyConverter();
StringBuffer buf = new StringBuffer();
converter.write(buf, le);
- // System.out.println(buf);
}
{
@@ -130,10 +126,10 @@
converter.setOptionList(this.optionList);
StringBuffer buf = new StringBuffer();
converter.write(buf, le);
- // System.out.println(buf);
}
}
+ @Test
public void testLogger() {
{
DynamicConverter<LoggingEvent> converter = new LoggerConverter();
@@ -151,35 +147,44 @@
converter.write(buf, le);
assertEquals("c.q.l.c.p.ConverterTest", buf.toString());
}
- }
- public void testClass() {
{
- DynamicConverter<LoggingEvent> converter = new ClassOfCallerConverter();
+ DynamicConverter<LoggingEvent> converter = new LoggerConverter();
+ this.optionList.clear();
+ this.optionList.add("0");
+ converter.setOptionList(this.optionList);
+ converter.start();
StringBuffer buf = new StringBuffer();
converter.write(buf, le);
- assertEquals(this.getClass().getName(), buf.toString());
+ assertEquals("ConverterTest", buf.toString());
}
}
+ @Test
+ public void testClass() {
+ DynamicConverter<LoggingEvent> converter = new ClassOfCallerConverter();
+ StringBuffer buf = new StringBuffer();
+ converter.write(buf, le);
+ assertEquals(this.getClass().getName(), buf.toString());
+ }
+
+ @Test
public void testMethodOfCaller() {
- {
- DynamicConverter<LoggingEvent> converter = new MethodOfCallerConverter();
- StringBuffer buf = new StringBuffer();
- converter.write(buf, le);
- assertEquals("testMethodOfCaller", buf.toString());
- }
+ DynamicConverter<LoggingEvent> converter = new MethodOfCallerConverter();
+ StringBuffer buf = new StringBuffer();
+ converter.write(buf, le);
+ assertEquals("testMethodOfCaller", buf.toString());
}
+ @Test
public void testFileOfCaller() {
- {
- DynamicConverter<LoggingEvent> converter = new FileOfCallerConverter();
- StringBuffer buf = new StringBuffer();
- converter.write(buf, le);
- assertEquals("ConverterTest.java", buf.toString());
- }
+ DynamicConverter<LoggingEvent> converter = new FileOfCallerConverter();
+ StringBuffer buf = new StringBuffer();
+ converter.write(buf, le);
+ assertEquals("ConverterTest.java", buf.toString());
}
+ @Test
public void testCallerData() {
{
DynamicConverter<LoggingEvent> converter = new CallerDataConverter();
@@ -262,42 +267,41 @@
}
+ @Test
public void testRelativeTime() throws Exception {
- {
- DynamicConverter<LoggingEvent> converter = new RelativeTimeConverter();
- StringBuffer buf0 = new StringBuffer();
- StringBuffer buf1 = new StringBuffer();
- LoggingEvent e0 = makeLoggingEvent(null);
- LoggingEvent e1 = makeLoggingEvent(null);
- converter.write(buf0, e0);
- converter.write(buf1, e1);
- assertEquals(buf0.toString(), buf1.toString());
- int rt0 = Integer.parseInt(buf0.toString());
- if (rt0 < 50) {
- fail("relative time should be > 50, but it is " + rt0);
- }
+ DynamicConverter<LoggingEvent> converter = new RelativeTimeConverter();
+ StringBuffer buf0 = new StringBuffer();
+ StringBuffer buf1 = new StringBuffer();
+ LoggingEvent e0 = makeLoggingEvent(null);
+ LoggingEvent e1 = makeLoggingEvent(null);
+ converter.write(buf0, e0);
+ converter.write(buf1, e1);
+ assertEquals(buf0.toString(), buf1.toString());
+ int rt0 = Integer.parseInt(buf0.toString());
+ if (rt0 < 50) {
+ fail("relative time should be > 50, but it is " + rt0);
}
}
+ @Test
public void testSyslogStart() throws Exception {
- {
- DynamicConverter<LoggingEvent> converter = new SyslogStartConverter();
- this.optionList.clear();
- this.optionList.add("MAIL");
- converter.setOptionList(this.optionList);
- converter.start();
+ DynamicConverter<LoggingEvent> converter = new SyslogStartConverter();
+ this.optionList.clear();
+ this.optionList.add("MAIL");
+ converter.setOptionList(this.optionList);
+ converter.start();
- LoggingEvent event = makeLoggingEvent(null);
+ LoggingEvent event = makeLoggingEvent(null);
- StringBuffer buf = new StringBuffer();
- converter.write(buf, event);
+ StringBuffer buf = new StringBuffer();
+ converter.write(buf, event);
- String expected = "<"
- + (SyslogConstants.LOG_MAIL + SyslogConstants.INFO_SEVERITY) + ">";
- assertTrue(buf.toString().startsWith(expected));
- }
+ String expected = "<"
+ + (SyslogConstants.LOG_MAIL + SyslogConstants.INFO_SEVERITY) + ">";
+ assertTrue(buf.toString().startsWith(expected));
}
+ @Test
public void testMDCConverter() throws Exception {
MDC.clear();
MDC.put("someKey", "someValue");
Modified: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/PackageTest.java
==============================================================================
--- logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/PackageTest.java (original)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/PackageTest.java Wed Nov 12 19:40:35 2008
@@ -10,6 +10,7 @@
package ch.qos.logback.classic.pattern;
+import junit.framework.JUnit4TestAdapter;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
@@ -18,8 +19,8 @@
public static Test suite() {
TestSuite suite = new TestSuite();
- suite.addTestSuite(ConverterTest.class);
- suite.addTestSuite(ClassNameAbbreviatorTest.class);
+ suite.addTest(new JUnit4TestAdapter(ConverterTest.class));
+ suite.addTest(new JUnit4TestAdapter(TargetLengthBasedClassNameAbbreviatorTest.class));
suite.addTestSuite(MDCConverterTest.class);
suite.addTestSuite(MarkerConverterTest.class);
return suite;
Copied: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/TargetLengthBasedClassNameAbbreviatorTest.java (from r1899, /logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/ClassNameAbbreviatorTest.java)
==============================================================================
--- /logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/ClassNameAbbreviatorTest.java (original)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/pattern/TargetLengthBasedClassNameAbbreviatorTest.java Wed Nov 12 19:40:35 2008
@@ -1,147 +1,143 @@
/**
- * LOGBack: the reliable, fast and flexible logging library for Java.
- *
- * Copyright (C) 1999-2006, QOS.ch
- *
- * This library is free software, you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation.
+ * Logback: the generic, reliable, fast and flexible logging framework.
+ *
+ * Copyright (C) 2000-2008, QOS.ch
+ *
+ * This library is free software, you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation.
*/
package ch.qos.logback.classic.pattern;
-import junit.framework.TestCase;
+import static org.junit.Assert.*;
-import ch.qos.logback.classic.pattern.ClassNameAbbreviator;
+import org.junit.Test;
-public class ClassNameAbbreviatorTest extends TestCase {
+import ch.qos.logback.classic.pattern.TargetLengthBasedClassNameAbbreviator;
- public ClassNameAbbreviatorTest(String arg0) {
- super(arg0);
- }
-
- protected void setUp() throws Exception {
- super.setUp();
- }
+public class TargetLengthBasedClassNameAbbreviatorTest {
- protected void tearDown() throws Exception {
- super.tearDown();
- }
+ @Test
public void testShortName() {
{
- ClassNameAbbreviator abbreviator = new ClassNameAbbreviator(100);
+ TargetLengthBasedClassNameAbbreviator abbreviator = new TargetLengthBasedClassNameAbbreviator(100);
String name = "hello";
assertEquals(name, abbreviator.abbreviate(name));
}
{
- ClassNameAbbreviator abbreviator = new ClassNameAbbreviator(100);
+ TargetLengthBasedClassNameAbbreviator abbreviator = new TargetLengthBasedClassNameAbbreviator(100);
String name = "hello.world";
assertEquals(name, abbreviator.abbreviate(name));
}
}
+ @Test
public void testNoDot() {
- ClassNameAbbreviator abbreviator = new ClassNameAbbreviator(1);
+ TargetLengthBasedClassNameAbbreviator abbreviator = new TargetLengthBasedClassNameAbbreviator(1);
String name = "hello";
assertEquals(name, abbreviator.abbreviate(name));
}
+ @Test
public void testOneDot() {
{
- ClassNameAbbreviator abbreviator = new ClassNameAbbreviator(1);
+ TargetLengthBasedClassNameAbbreviator abbreviator = new TargetLengthBasedClassNameAbbreviator(1);
String name = "hello.world";
assertEquals("h.world", abbreviator.abbreviate(name));
}
{
- ClassNameAbbreviator abbreviator = new ClassNameAbbreviator(1);
+ TargetLengthBasedClassNameAbbreviator abbreviator = new TargetLengthBasedClassNameAbbreviator(1);
String name = "h.world";
assertEquals("h.world", abbreviator.abbreviate(name));
}
{
- ClassNameAbbreviator abbreviator = new ClassNameAbbreviator(1);
+ TargetLengthBasedClassNameAbbreviator abbreviator = new TargetLengthBasedClassNameAbbreviator(1);
String name = ".world";
assertEquals(".world", abbreviator.abbreviate(name));
}
}
+ @Test
public void testTwoDot() {
{
- ClassNameAbbreviator abbreviator = new ClassNameAbbreviator(1);
+ TargetLengthBasedClassNameAbbreviator abbreviator = new TargetLengthBasedClassNameAbbreviator(1);
String name = "com.logback.Foobar";
assertEquals("c.l.Foobar", abbreviator.abbreviate(name));
}
{
- ClassNameAbbreviator abbreviator = new ClassNameAbbreviator(1);
+ TargetLengthBasedClassNameAbbreviator abbreviator = new TargetLengthBasedClassNameAbbreviator(1);
String name = "c.logback.Foobar";
assertEquals("c.l.Foobar", abbreviator.abbreviate(name));
}
{
- ClassNameAbbreviator abbreviator = new ClassNameAbbreviator(1);
+ TargetLengthBasedClassNameAbbreviator abbreviator = new TargetLengthBasedClassNameAbbreviator(1);
String name = "c..Foobar";
assertEquals("c..Foobar", abbreviator.abbreviate(name));
}
{
- ClassNameAbbreviator abbreviator = new ClassNameAbbreviator(1);
+ TargetLengthBasedClassNameAbbreviator abbreviator = new TargetLengthBasedClassNameAbbreviator(1);
String name = "..Foobar";
assertEquals("..Foobar", abbreviator.abbreviate(name));
}
}
-
+
+ @Test
public void test3Dot() {
{
- ClassNameAbbreviator abbreviator = new ClassNameAbbreviator(1);
+ TargetLengthBasedClassNameAbbreviator abbreviator = new TargetLengthBasedClassNameAbbreviator(1);
String name = "com.logback.xyz.Foobar";
assertEquals("c.l.x.Foobar", abbreviator.abbreviate(name));
}
{
- ClassNameAbbreviator abbreviator = new ClassNameAbbreviator(13);
+ TargetLengthBasedClassNameAbbreviator abbreviator = new TargetLengthBasedClassNameAbbreviator(13);
String name = "com.logback.xyz.Foobar";
assertEquals("c.l.x.Foobar", abbreviator.abbreviate(name));
}
{
- ClassNameAbbreviator abbreviator = new ClassNameAbbreviator(14);
+ TargetLengthBasedClassNameAbbreviator abbreviator = new TargetLengthBasedClassNameAbbreviator(14);
String name = "com.logback.xyz.Foobar";
assertEquals("c.l.xyz.Foobar", abbreviator.abbreviate(name));
}
{
- ClassNameAbbreviator abbreviator = new ClassNameAbbreviator(15);
+ TargetLengthBasedClassNameAbbreviator abbreviator = new TargetLengthBasedClassNameAbbreviator(15);
String name = "com.logback.alligator.Foobar";
assertEquals("c.l.a.Foobar", abbreviator.abbreviate(name));
}
}
-
+ @Test
public void testXDot() {
{
- ClassNameAbbreviator abbreviator = new ClassNameAbbreviator(21);
+ TargetLengthBasedClassNameAbbreviator abbreviator = new TargetLengthBasedClassNameAbbreviator(21);
String name = "com.logback.wombat.alligator.Foobar";
assertEquals("c.l.w.a.Foobar", abbreviator.abbreviate(name));
}
{
- ClassNameAbbreviator abbreviator = new ClassNameAbbreviator(22);
+ TargetLengthBasedClassNameAbbreviator abbreviator = new TargetLengthBasedClassNameAbbreviator(22);
String name = "com.logback.wombat.alligator.Foobar";
assertEquals("c.l.w.alligator.Foobar", abbreviator.abbreviate(name));
}
{
- ClassNameAbbreviator abbreviator = new ClassNameAbbreviator(1);
+ TargetLengthBasedClassNameAbbreviator abbreviator = new TargetLengthBasedClassNameAbbreviator(1);
String name = "com.logback.wombat.alligator.tomato.Foobar";
assertEquals("c.l.w.a.t.Foobar", abbreviator.abbreviate(name));
}
{
- ClassNameAbbreviator abbreviator = new ClassNameAbbreviator(21);
+ TargetLengthBasedClassNameAbbreviator abbreviator = new TargetLengthBasedClassNameAbbreviator(21);
String name = "com.logback.wombat.alligator.tomato.Foobar";
assertEquals("c.l.w.a.tomato.Foobar", abbreviator.abbreviate(name));
}
{
- ClassNameAbbreviator abbreviator = new ClassNameAbbreviator(29);
+ TargetLengthBasedClassNameAbbreviator abbreviator = new TargetLengthBasedClassNameAbbreviator(29);
String name = "com.logback.wombat.alligator.tomato.Foobar";
assertEquals("c.l.w.alligator.tomato.Foobar", abbreviator.abbreviate(name));
}
Modified: logback/trunk/logback-site/src/site/pages/css/common.css
==============================================================================
--- logback/trunk/logback-site/src/site/pages/css/common.css (original)
+++ logback/trunk/logback-site/src/site/pages/css/common.css Wed Nov 12 19:40:35 2008
@@ -124,6 +124,11 @@
}
+/* apply to tr elements of tables which are both bodytable and dark */
+table[class="bodyTable dark"] tr {
+ background-color: #ddd;
+}
+
table.bodyTable tr.a {
background-color: #ddd;
}
@@ -178,6 +183,7 @@
color: red;
font-weight: bold;
}
+
.greenBold {
color: green;
font-weight: bold;
Modified: logback/trunk/logback-site/src/site/pages/manual/layouts.html
==============================================================================
--- logback/trunk/logback-site/src/site/pages/manual/layouts.html (original)
+++ logback/trunk/logback-site/src/site/pages/manual/layouts.html Wed Nov 12 19:40:35 2008
@@ -208,7 +208,8 @@
the logging request was sent.
</p>
<p>Here is the implementation of this class:</p>
-<div class="source"><pre>package chapter5;
+
+ <p class="source">package chapter5;
import ch.qos.logback.classic.spi.LoggingEvent;
import ch.qos.logback.core.LayoutBase;
@@ -247,7 +248,7 @@
sbuf.append(LINE_SEP);
return sbuf.toString();
}
-}</pre></div>
+}</p>
<p>The addition of the corresponding setter method is all that is
@@ -260,7 +261,7 @@
</p>
-<div class="source"><pre><configuration>
+ <p class="source"><configuration>
<appender name="STDOUT"
class="ch.qos.logback.core.ConsoleAppender">
@@ -274,25 +275,22 @@
<level value="debug" />
<appender-ref ref="STDOUT" />
</root>
-</configuration></pre></div>
+</configuration></p>
- <p>
-
- </p>
+ <p></p>
<h3><a name="ClassicPatternLayout"
href="#ClassicPatternLayout">PatternLayout</a></h3>
- <p>
- Logback classic ships with a flexible layout called <a
- href="../xref/ch/qos/logback/classic/PatternLayout.html">
- <code>PatternLayout</code></a>. As all layouts,
- <code>PatternLayout</code> takes a logging event and returns a
- <code>String</code>. However, this <code>String</code> can be
- customized at will by tweaking the conversion pattern of
- <code>PatternLayout</code>.
+ <p>Logback classic ships with a flexible layout called <a
+ href="../xref/ch/qos/logback/classic/PatternLayout.html">
+ <code>PatternLayout</code></a>. As all layouts,
+ <code>PatternLayout</code> takes a logging event and returns a
+ <code>String</code>. However, this <code>String</code> can be
+ customized at will by tweaking the conversion pattern of
+ <code>PatternLayout</code>.
</p>
<p>The conversion pattern of <code>PatternLayout</code> is closely
@@ -301,7 +299,7 @@
composed of literal text and format control expressions called
conversion specifiers. You are free to insert any literal text
within the conversion pattern. Each conversion specifier starts
- with a percent sign (%) and is followed by optional format
+ with a percent sign '%' and is followed by optional format
modifiers, a conversion word and optional parameters between
braces. The conversion word controls the type of data to use, e.g.
logger name, level, date, thread name. The format modifiers
@@ -341,25 +339,23 @@
}
}</pre></div>
- <p>
- The conversion pattern is set to be <b>"%-5level [%thread]:
- %message%n"</b>. Running PatternSample will yield the following
- output on the console.
- </p>
- <div class="source"><pre>DEBUG [main]: Message 1
-WARN [main]: Message 2</pre></div>
- <p>
- Note that in the conversion pattern <b>"%-5level [%thread]:
- %message%n"</b> there is no explicit separator between literal
- text and conversion specifiers. When parsing a conversion
- pattern,
- <code>PatternLayout</code>
- is capable of differentiating between literal text (space
- characters, the brackets, colon character) and conversion
- specifiers. In the example above, the conversion specifier
- %-5level means the level of the logging event should be left
- justified to a width of five characters. Format specifiers
- will be explained in a short moment.
+ <p>The conversion pattern is set to be <b>"%-5level [%thread]:
+ %message%n"</b>. Running PatternSample will yield the following
+ output on the console.
+ </p>
+
+ <p class="source">DEBUG [main]: Message 1
+WARN [main]: Message 2</p>
+
+ <p>Note that in the conversion pattern <b>"%-5level [%thread]:
+ %message%n"</b> there is no explicit separator between literal
+ text and conversion specifiers. When parsing a conversion pattern,
+ <code>PatternLayout</code> is capable of differentiating between
+ literal text (space characters, the brackets, colon character) and
+ conversion specifiers. In the example above, the conversion
+ specifier %-5level means the level of the logging event should be
+ left justified to a width of five characters. Format specifiers
+ will be explained in a short moment.
</p>
<p>In PatternLayout, parenthesis can be used to group conversion
@@ -402,43 +398,52 @@
the logging event.
</p>
- <p>This conversion word can take an integer as first and
+ <p>This conversion word can take an integer as its first and
only option. The converter's abbreviation algorithm will
shorten the logger name, usually without significant loss of
- meaning. The next table provides examples of the
- abbreviation algorithm in action.
+ meaning. Setting the value of this option to zero will cause
+ the conversoin specifier to return the string right to the
+ rightmost dot character. The next table provides examples of
+ the abbreviation algorithm in action.
</p>
- <table class="bodyTable" border="0" cellpadding="8">
- <tr class="a">
+ <table class="bodyTable dark" border="0" cellpadding="8">
+ <tr>
<th>Conversion specifier</th>
<th>Logger name</th>
<th>Result</th>
</tr>
- <tr class="a">
+ <tr>
<td>%logger</td>
<td>mainPackage.sub.sample.Bar</td>
<td>mainPackage.sub.sample.Bar</td>
</tr>
- <tr class="a">
+
+ <tr>
+ <td>%logger{0}</td>
+ <td>mainPackage.sub.sample.Bar</td>
+ <td>Bar</td>
+ </tr>
+
+ <tr>
<td>%logger{10}</td>
<td>mainPackage.sub.sample.Bar</td>
<td>m.s.s.Bar</td>
</tr>
- <tr class="a">
+ <tr>
<td>%logger{15}</td>
<td>mainPackage.sub.sample.Bar</td>
<td>m.s.sample.Bar</td>
</tr>
- <tr class="a">
+ <tr>
<td>%logger{16}</td>
<td>mainPackage.sub.sample.Bar</td>
<td>m.sub.sample.Bar</td>
</tr>
- <tr class="a">
+ <tr>
<td>%logger{26}</td>
<td>mainPackage.sub.sample.Bar</td>
<td>mainPackage.sub.sample.Bar</td>
@@ -459,12 +464,15 @@
</p>
<p> Just like the <em>%logger</em> conversion word above,
this word can take an interger as it's first option and use
- its abbreviation algorithm to shorten the class name. By
+ its abbreviation algorithm to shorten the class name. Zero
+ carries special meaning and w will cause the simple class
+ name to be output without its package name prefix. By
default the class name is output in full.
</p>
- <p>Generating the caller class information is not
- particularly fast. Thus, it's use should be avoided unless
- execution speed is not an issue.
+
+ <p>Generating the caller class information is not
+ particularly fast. Thus, it's use should be avoided unless
+ execution speed is not an issue.
</p>
</td>
</tr>
@@ -491,24 +499,24 @@
actual date is Friday 20th of October, 2006 and that the
author finished his meal a short while ago.</p>
- <table class="bodyTable" cellpadding="8">
- <tr class="a">
+ <table class="bodyTable dark" cellpadding="8">
+ <tr>
<th>Conversion Pattern</th>
<th>Result</th>
</tr>
- <tr class="a">
+ <tr>
<td>%date</td>
<td>2006-10-20 14:46:49,812</td>
</tr>
- <tr class="a">
+ <tr>
<td>%date{ISO8601}</td>
<td>2006-10-20 14:46:49,812</td>
</tr>
- <tr class="a">
+ <tr>
<td>%date{HH:mm:ss.SSS}</td>
<td>14:46:49.812</td>
</tr>
- <tr class="a">
+ <tr>
<td>%date{dd MMM yyyy ;HH:mm:ss.SSS}</td>
<td>20 oct. 2006;14:46:49.812 </td>
</tr>
@@ -522,7 +530,7 @@
</td>
<td>
- <p> Used to output the file name of the Java source file
+ <p>Used to output the file name of the Java source file
where the logging request was issued.
</p>
Modified: logback/trunk/logback-site/src/site/pages/news.html
==============================================================================
--- logback/trunk/logback-site/src/site/pages/news.html (original)
+++ logback/trunk/logback-site/src/site/pages/news.html Wed Nov 12 19:40:35 2008
@@ -45,29 +45,35 @@
<code>NullPointerException</code> would be thrown.
</p>
- <p>In reponse to <a
- href="http://jira.qos.ch/browse/LBCLASSIC-61">LBCLASSIC-61</a>, <a
- href="http://jira.qos.ch/browse/LBCLASSIC-33">LBCLASSIC-33</a>, <a
- href="http://jira.qos.ch/browse/LBCLASSIC-14">LBCLASSIC-24</a> and
- <a href="http://jira.qos.ch/browse/LBCLASSIC-24">LBCLASSIC-14</a>
- JMXConfigurator has been redesigned.
- </p>
+ <p>In reponse to <a
+ href="http://jira.qos.ch/browse/LBCLASSIC-61">LBCLASSIC-61</a>, <a
+ href="http://jira.qos.ch/browse/LBCLASSIC-33">LBCLASSIC-33</a>, <a
+ href="http://jira.qos.ch/browse/LBCLASSIC-14">LBCLASSIC-24</a> and
+ <a href="http://jira.qos.ch/browse/LBCLASSIC-24">LBCLASSIC-14</a>
+ JMXConfigurator has been redesigned.
+ </p>
- <p>Fixed improvement request <a
- href="http://jira.qos.ch/browse/LBCLASSIC-59">LBCLASSIC-59</a>
- relation to StatusListeners, originally submitted by Anton Tagunov.
- </p>
+ <p>Fixed improvement request <a
+ href="http://jira.qos.ch/browse/LBCLASSIC-59">LBCLASSIC-59</a>
+ relation to StatusListeners, originally submitted by Anton Tagunov.
+ </p>
- <p>In response to <a href="http://jira.qos.ch/browse/LBCLASSIC-54">
- LBCLASSIC-54</a> support for turbo filters has refactored. The
- present code is safe under concurrent access while still offering
- good performance.
- </p>
+ <p>The logger and class name converters now consider zero as
+ having special meaning, and will return the simple class name,
+ removing the package name prefix. This feature was asked by
+ Silvano Maffeis.</p>
+
+ <p>In response to <a
+ href="http://jira.qos.ch/browse/LBCLASSIC-54"> LBCLASSIC-54</a>
+ support for turbo filters has refactored. The present code is safe
+ under concurrent access while still offering good performance.
+ </p>
- <p>Fixed <a href="http://jira.qos.ch/browse/LBCORE-43">LBCORE-43</a>
- reported by Bruno Navert. Configuration files can now look up
- property files from classpath resources.
- </p>
+ <p>Fixed <a
+ href="http://jira.qos.ch/browse/LBCORE-43">LBCORE-43</a> reported
+ by Bruno Navert. Configuration files can now look up property
+ files from classpath resources.
+ </p>
<!-- ======================== minor ================== -->
1
0
Author: ceki
Date: Wed Nov 12 16:26:28 2008
New Revision: 1966
Modified:
logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/LoggerContext.java
logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/jmx/JMXConfigurator.java
logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/jmx/MBeanUtil.java
logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/JMXConfiguratorAction.java
logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/selector/servlet/ContextDetachingSCL.java
logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerContextListener.java
logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/TurboFilteringInLoggerTest.java
logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/db/DBAppenderIntegrationTest.java
logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/jmx/JMXConfiguratorTest.java
logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/spi/BasicContextListener.java
logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/spi/ContextListenerTest.java
logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/util/InitializationTest.java
logback/trunk/logback-examples/src/main/java/chapter4/ExitWoes2.java
logback/trunk/logback-examples/src/main/java/chapter4/socket/SocketClient2.java
logback/trunk/logback-examples/src/main/java/chapter7/SimpleMDC.java
logback/trunk/logback-site/src/site/pages/manual/appenders.html
logback/trunk/logback-site/src/site/pages/manual/jmxConfig.html
logback/trunk/logback-site/src/site/pages/manual/joran.html
logback/trunk/logback-site/src/site/pages/manual/mdc.html
logback/trunk/logback-site/src/site/pages/news.html
Log:
While working on JMXConfigurator, it became apparent that
some components needed to survive resetting of the context.
For example, when the JMXConfigrator.reload*() methods are invoked, if
the JMXConfigurator instance is unregistered from the MBeans server
while the reload*() method is still running, then a
NullPointerException is thrown from the JMX server. Thus,
JMXConfigurator needs to survive the resetting of the LoggerContext it
is attached to. However, when the host application is stopped (or
restarted), the JMXConfigrator needs to be unregistered from the
MBeans server, otherwise severe memory leaks occur.
Consequently, LoggerContext now offers two related but distinct
methods, reset() and stop(). The former should be called if you just
wish to reset the configuration of the logger context. The former
should be called when you wish to completely stop the LoggerContext so
that it can not be reused ever again.
The LoggerContext.shutdownAndReset method no longer exists. (Simply
deprecating in favor of stop() or reset() seemed too error prone.)
Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/LoggerContext.java
==============================================================================
--- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/LoggerContext.java (original)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/LoggerContext.java Wed Nov 12 16:26:28 2008
@@ -63,9 +63,9 @@
boolean started = false;
+
public LoggerContext() {
super();
- System.out.println(" LoggerContext()=============================================================");
this.loggerCache = new Hashtable<String, Logger>();
this.loggerContextRemoteView = new LoggerContextRemoteView(this);
this.root = new Logger(ROOT_NAME, null, this);
@@ -190,25 +190,15 @@
return loggerContextRemoteView;
}
- @Override
- protected void finalize() {
- System.out.println("**************** LoggerContext finalized");
- }
+ /**
+ * This method closes all appenders,
+ */
public void reset() {
root.recursiveReset();
clearAllTurboFilters();
fireOnReset();
- // TODO is it a good idea to reset the status listeners?
+ resetListenersExceptResetResistant();
resetStatusListeners();
- resetListeners();
- }
-
- /**
- * @deprecated Please use reset() method instead
- */
-
- public void shutdownAndReset() {
- reset();
}
private void resetStatusListeners() {
@@ -269,7 +259,18 @@
loggerContextListenerList.remove(listener);
}
- private void resetListeners() {
+ private void resetListenersExceptResetResistant() {
+ List<LoggerContextListener> toRetain = new ArrayList<LoggerContextListener>();
+
+ for(LoggerContextListener lcl: loggerContextListenerList) {
+ if(lcl.isResetResistant()) {
+ toRetain.add(lcl);
+ }
+ }
+ loggerContextListenerList.retainAll(toRetain);
+ }
+
+ private void resetAllListeners() {
loggerContextListenerList.clear();
}
@@ -289,6 +290,12 @@
}
}
+ private void fireOnStop() {
+ for (LoggerContextListener listener : loggerContextListenerList) {
+ listener.onStop(this);
+ }
+ }
+
// === end listeners ==============================================
public boolean isStarted() {
@@ -302,11 +309,13 @@
public void stop() {
reset();
+ fireOnStop();
+ resetAllListeners();
started = false;
}
-// @Override
-// public String toString() {
-// return this.getClass().getName() + "[" + getName() + "]";
-// }
+ @Override
+ public String toString() {
+ return this.getClass().getName() + "[" + getName() + "]";
+ }
}
Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/jmx/JMXConfigurator.java
==============================================================================
--- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/jmx/JMXConfigurator.java (original)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/jmx/JMXConfigurator.java Wed Nov 12 16:26:28 2008
@@ -56,39 +56,42 @@
MBeanServer mbs;
ObjectName objectName;
String objectNameAsString;
-
+
+ // whether to output status events on the console when reloading the
+ // configuration
+ boolean debug = true;
+
boolean started;
- @Override
- public void finalize() {
- System.out.println("....... finalize() "+this);
- }
-
public JMXConfigurator(LoggerContext loggerContext, MBeanServer mbs,
ObjectName objectName) {
- System.out.println("....... constructor() "+this);
started = true;
this.context = loggerContext;
this.loggerContext = loggerContext;
this.mbs = mbs;
this.objectName = objectName;
this.objectNameAsString = objectName.toString();
- removePreviousInstanceAsListener();
- loggerContext.addListener(this);
-
+ if (previouslyRegisteredListenerWithSameObjectName()) {
+ addError("Previously registered JMXConfigurator named ["
+ + objectNameAsString + "] in the logger context named ["
+ + loggerContext.getName() + "]");
+ } else {
+ // register as a listener only if there are no homonyms
+ loggerContext.addListener(this);
+ }
}
- private void removePreviousInstanceAsListener() {
+ private boolean previouslyRegisteredListenerWithSameObjectName() {
List<LoggerContextListener> lcll = loggerContext.getCopyOfListenerList();
for (LoggerContextListener lcl : lcll) {
if (lcl instanceof JMXConfigurator) {
JMXConfigurator jmxConfigurator = (JMXConfigurator) lcl;
if (objectName.equals(jmxConfigurator.objectName)) {
- addInfo("Removing previous JMXConfigurator from the logger context listener list");
- loggerContext.removeListener(lcl);
+ return true;
}
}
}
+ return false;
}
public void reloadDefaultConfiguration() throws JoranException {
@@ -121,23 +124,31 @@
StatusManager sm = loggerContext.getStatusManager();
sm.add(statusListener);
}
-
+
+ void removeStatusListener(StatusListener statusListener) {
+ StatusManager sm = loggerContext.getStatusManager();
+ sm.remove(statusListener);
+ }
+
public void reloadByURL(URL url) throws JoranException {
StatusListenerAsList statusListenerAsList = new StatusListenerAsList();
addStatusListener(statusListenerAsList);
addInfo("Resetting context: " + loggerContext.getName());
loggerContext.reset();
+ // after a reset the statusListenerAsList gets removed as a listener
addStatusListener(statusListenerAsList);
-
try {
JoranConfigurator configurator = new JoranConfigurator();
configurator.setContext(loggerContext);
configurator.doConfigure(url);
addInfo("Context: " + loggerContext.getName() + " reloaded.");
} finally {
- StatusPrinter.print(statusListenerAsList.getStatusList());
+ removeStatusListener(statusListenerAsList);
+ if (debug) {
+ StatusPrinter.print(statusListenerAsList.getStatusList());
+ }
}
}
@@ -219,23 +230,23 @@
}
/**
- * When the associated LoggerContext is reset, this configurator must be
+ * When the associated LoggerContext is stopped, this configurator must be
* unregistered
*/
- public void onReset(LoggerContext context) {
- if(!started) {
- addInfo("onReset() method called on a stopped JMXActivator [" + objectNameAsString + "]");;
+ public void onStop(LoggerContext context) {
+ if (!started) {
+ addInfo("onStop() method called on a stopped JMXActivator ["
+ + objectNameAsString + "]");
+ return;
}
- System.out.println("Unregistering JMXConfigurator");
-
if (mbs.isRegistered(objectName)) {
try {
addInfo("Unregistering mbean [" + objectNameAsString + "]");
mbs.unregisterMBean(objectName);
} catch (InstanceNotFoundException e) {
// this is theoretically impossible
- addError("Unable to find a verifiably registered mbean [" + objectNameAsString
- + "]", e);
+ addError("Unable to find a verifiably registered mbean ["
+ + objectNameAsString + "]", e);
} catch (MBeanRegistrationException e) {
addError("Failed to unregister [" + objectNameAsString + "]", e);
}
@@ -245,9 +256,21 @@
}
stop();
}
-
+
+ public void onReset(LoggerContext context) {
+ addInfo("onReset() method called JMXActivator [" + objectNameAsString + "]");
+ }
+
+ /**
+ * JMXConfigrator should not be removed subsequent to a LoggerContext reset.
+ *
+ * @return
+ */
+ public boolean isResetResistant() {
+ return true;
+ }
+
private void clearFields() {
- System.out.println("Clearing fields");
mbs = null;
objectName = null;
loggerContext = null;
@@ -257,12 +280,13 @@
started = false;
clearFields();
}
+
public void onStart(LoggerContext context) {
// nop
}
-// @Override
-// public String toString() {
-// return this.getClass().getName() + "(" + context.getName() + ")";
-// }
+ @Override
+ public String toString() {
+ return this.getClass().getName() + "(" + context.getName() + ")";
+ }
}
Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/jmx/MBeanUtil.java
==============================================================================
--- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/jmx/MBeanUtil.java (original)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/jmx/MBeanUtil.java Wed Nov 12 16:26:28 2008
@@ -9,8 +9,6 @@
*/
package ch.qos.logback.classic.jmx;
-import java.lang.management.ManagementFactory;
-
import javax.management.InstanceNotFoundException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
@@ -25,8 +23,8 @@
static final String DOMAIN = "ch.qos.logback.classic";
- static public String getObjectNameFor(Context context, Class type) {
- String objectNameAsStr = DOMAIN + ":Name=" + context.getName() + ",Type="
+ static public String getObjectNameFor(String contextName, Class type) {
+ String objectNameAsStr = DOMAIN + ":Name=" + contextName + ",Type="
+ type.getName();
return objectNameAsStr;
}
@@ -46,25 +44,17 @@
}
}
- public static JMXConfigurator register(LoggerContext loggerContext,
- ObjectName objectName, Object caller) {
- try {
- MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
-
- JMXConfigurator jmxConfigurator = new JMXConfigurator(loggerContext, mbs,
- objectName);
+ public static boolean isRegistered(MBeanServer mbs, ObjectName objectName) {
+ return mbs.isRegistered(objectName);
+ }
- if (mbs.isRegistered(objectName)) {
- StatusUtil.addWarn(loggerContext, caller,
- "Unregistering existing MBean named ["
- + objectName.getCanonicalName() + "]");
- mbs.unregisterMBean(objectName);
- }
+ public static void createAndRegisterJMXConfigurator(
+ MBeanServer mbs, LoggerContext loggerContext,
+ JMXConfigurator jmxConfigurator, ObjectName objectName, Object caller) {
+ try {
mbs.registerMBean(jmxConfigurator, objectName);
- return jmxConfigurator;
} catch (Exception e) {
StatusUtil.addError(loggerContext, caller, "Failed to create mbean", e);
- return null;
}
}
Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/JMXConfiguratorAction.java
==============================================================================
--- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/JMXConfiguratorAction.java (original)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/JMXConfiguratorAction.java Wed Nov 12 16:26:28 2008
@@ -1,5 +1,8 @@
package ch.qos.logback.classic.joran.action;
+import java.lang.management.ManagementFactory;
+
+import javax.management.MBeanServer;
import javax.management.ObjectName;
import org.xml.sax.Attributes;
@@ -15,7 +18,7 @@
public class JMXConfiguratorAction extends Action {
static final String OBJECT_NAME_ATTRIBUTE_NAME = "objectName";
- static final String SUFFIX_ATTRIBUTE_NAME = "suffix";
+ static final String CONTEXT_NAME_ATTRIBUTE_NAME = "contextName";
static final char JMX_NAME_SEPARATOR = ',';
@Override
@@ -23,31 +26,44 @@
throws ActionException {
addInfo("begin");
+
+ String contextName = context.getName();
+ String contextNameAttributeVal = attributes
+ .getValue(CONTEXT_NAME_ATTRIBUTE_NAME);
+ if(!OptionHelper.isEmpty(contextNameAttributeVal)) {
+ contextName = contextNameAttributeVal;
+ }
+
String objectNameAsStr;
String objectNameAttributeVal = attributes
.getValue(OBJECT_NAME_ATTRIBUTE_NAME);
- String suffixAttributeVal = attributes
- .getValue(SUFFIX_ATTRIBUTE_NAME);
-
if (OptionHelper.isEmpty(objectNameAttributeVal)) {
- objectNameAsStr = MBeanUtil.getObjectNameFor((LoggerContext) context,
+ objectNameAsStr = MBeanUtil.getObjectNameFor(contextName,
JMXConfigurator.class);
} else {
objectNameAsStr = objectNameAttributeVal;
}
- if(!OptionHelper.isEmpty(suffixAttributeVal)) {
- if(suffixAttributeVal.indexOf(0) != JMX_NAME_SEPARATOR) {
- objectNameAsStr += JMX_NAME_SEPARATOR;
- }
- objectNameAsStr += suffixAttributeVal;
- }
-
ObjectName objectName = MBeanUtil.string2ObjectName(context, this,
objectNameAsStr);
-
- if (objectName != null) {
- MBeanUtil.register((LoggerContext) context, objectName, this);
+ if (objectName == null) {
+ addError("Failed to for ObjectName for ["+objectNameAsStr+"]");
+ return;
+ }
+
+ MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+ if(!MBeanUtil.isRegistered(mbs, objectName)) {
+ // register only of the named JMXConfigurator has not been previously
+ // registered. Unregistering an MBean within invocation of itself
+ // caused jconsole to throw an NPE. (This occurs when the reload* method
+ // unregisters the
+ JMXConfigurator jmxConfigurator = new JMXConfigurator((LoggerContext) context, mbs,
+ objectName);
+ try {
+ mbs.registerMBean(jmxConfigurator, objectName);
+ } catch (Exception e) {
+ addError("Failed to create mbean", e);
+ }
}
}
Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/selector/servlet/ContextDetachingSCL.java
==============================================================================
--- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/selector/servlet/ContextDetachingSCL.java (original)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/selector/servlet/ContextDetachingSCL.java Wed Nov 12 16:26:28 2008
@@ -42,8 +42,9 @@
LoggerContext context = selector.detachLoggerContext(loggerContextName);
if (context != null) {
Logger logger = context.getLogger(LoggerContext.ROOT_NAME);
- logger.warn("Shutting down context " + loggerContextName);
- context.reset();
+ logger.warn("Stopping logger context " + loggerContextName);
+ // when the web-app is destroyed, its logger context should be stopped
+ context.stop();
} else {
System.out.println("No context named " + loggerContextName + " was found.");
}
Modified: logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerContextListener.java
==============================================================================
--- logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerContextListener.java (original)
+++ logback/trunk/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerContextListener.java Wed Nov 12 16:26:28 2008
@@ -13,7 +13,14 @@
public interface LoggerContextListener {
- public void onReset(LoggerContext context);
- public void onStart(LoggerContext context);
+ /**
+ * Some listeners should not be removed when the LoggerContext is
+ * reset. Such listeners are said to be reset resistant.
+ * @return whether this listener is reset resistant or not.
+ */
+ public boolean isResetResistant();
+ public void onStart(LoggerContext context);
+ public void onReset(LoggerContext context);
+ public void onStop(LoggerContext context);
}
Modified: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/TurboFilteringInLoggerTest.java
==============================================================================
--- logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/TurboFilteringInLoggerTest.java (original)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/TurboFilteringInLoggerTest.java Wed Nov 12 16:26:28 2008
@@ -14,7 +14,7 @@
LoggerContext context;
Logger logger;
Marker blueMarker = MarkerFactory.getMarker(BLUE);
-
+
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -40,7 +40,7 @@
filter.start();
context.addTurboFilter(filter);
}
-
+
private void addAcceptBLUEFilter() {
MarkerFilter filter = new MarkerFilter();
filter.setMarker(BLUE);
@@ -48,7 +48,7 @@
filter.start();
context.addTurboFilter(filter);
}
-
+
private void addDenyBLUEFilter() {
MarkerFilter filter = new MarkerFilter();
filter.setMarker(BLUE);
@@ -56,8 +56,6 @@
filter.start();
context.addTurboFilter(filter);
}
-
-
public void testIsDebugEnabledWithYesFilter() {
addYesFilter();
@@ -131,13 +129,13 @@
assertFalse(logger.isDebugEnabled(blueMarker));
}
- public void testLoggingContextShutdownAndReset() {
+ public void testLoggingContextReset() {
addYesFilter();
assertNotNull(context.getTurboFilterList().get(0));
context.reset();
assertEquals(0, context.getTurboFilterList().size());
}
-
+
}
class YesFilter extends TurboFilter {
Modified: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/db/DBAppenderIntegrationTest.java
==============================================================================
--- logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/db/DBAppenderIntegrationTest.java (original)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/db/DBAppenderIntegrationTest.java Wed Nov 12 16:26:28 2008
@@ -46,7 +46,8 @@
@After
public void tearDown() throws Exception {
- lc.reset();
+ // lc will never be used again
+ lc.stop();
}
public void doTest(String configFile) throws JoranException {
Modified: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/jmx/JMXConfiguratorTest.java
==============================================================================
--- logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/jmx/JMXConfiguratorTest.java (original)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/jmx/JMXConfiguratorTest.java Wed Nov 12 16:26:28 2008
@@ -2,6 +2,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -23,21 +24,23 @@
public class JMXConfiguratorTest {
- static MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
+ MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
LoggerContext lc = new LoggerContext();
Logger testLogger = lc.getLogger(this.getClass());
List<LoggerContextListener> listenerList;
int diff = RandomUtil.getPositiveInt();
+
@Before
public void setUp() throws Exception {
lc.setName("context-" + diff);
+ assertNotNull(mbs);
}
@After
public void tearDown() throws Exception {
- lc.reset();
+ lc.stop();
}
@Override
@@ -46,45 +49,69 @@
}
@Test
- public void contextListening() {
- String objectNameAsStr = "ch.qos.logback.toto" + ":Name=" + lc.getName()
+ public void contextReset() throws Exception {
+ String randomizedObjectNameAsStr = "ch.qos.logback."+diff + ":Name=" + lc.getName()
+ ",Type=" + this.getClass().getName();
- ObjectName on = MBeanUtil.string2ObjectName(lc, this, objectNameAsStr);
- MBeanUtil.register(lc, on, this);
+ ObjectName objectName = MBeanUtil.string2ObjectName(lc, this, randomizedObjectNameAsStr);
+ JMXConfigurator jmxConfigurator = new JMXConfigurator(lc, mbs, objectName);
+ mbs.registerMBean(jmxConfigurator, objectName);
+
listenerList = lc.getCopyOfListenerList();
assertEquals(1, listenerList.size());
+
lc.reset();
+
+ // check that after lc.reset, jmxConfigurator should still be
+ // registered as a listener in the loggerContext and also as an
+ // MBean in mbs
listenerList = lc.getCopyOfListenerList();
- assertEquals(0, listenerList.size());
+ assertEquals(1, listenerList.size());
+ assertTrue(listenerList.contains(jmxConfigurator));
- MBeanUtil.register(lc, on, this);
+ assertTrue(mbs.isRegistered(objectName));
+ }
+
+ @Test
+ public void contextStop() throws Exception {
+ String randomizedObjectNameAsStr = "ch.qos.logback."+diff + ":Name=" + lc.getName()
+ + ",Type=" + this.getClass().getName();
+
+ ObjectName objectName = MBeanUtil.string2ObjectName(lc, this, randomizedObjectNameAsStr);
+ JMXConfigurator jmxConfigurator = new JMXConfigurator(lc, mbs, objectName);
+ mbs.registerMBean(jmxConfigurator, objectName);
+
listenerList = lc.getCopyOfListenerList();
assertEquals(1, listenerList.size());
+
+ lc.stop();
+
+ // check that after lc.stop, jmxConfigurator is no longer
+ // registered as a listener in the loggerContext nor as an
+ // MBean in mbs
+ listenerList = lc.getCopyOfListenerList();
+ assertEquals(0, listenerList.size());
+ assertFalse(mbs.isRegistered(objectName));
}
@Test
- public void testRemovalOfPreviousIntanceFromTheContextListenerList() {
+ public void testNonRemovalOfPreviousIntanceFromTheContextListenerList() {
String objectNameAsStr = "ch.qos.logback.toto" + ":Name=" + lc.getName()
+ ",Type=" + this.getClass().getName();
-
- ObjectName on = MBeanUtil.string2ObjectName(lc, this, objectNameAsStr);
- JMXConfigurator jmxConfigurator0 = MBeanUtil.register(lc, on, this);
-
+ ObjectName objectName = MBeanUtil.string2ObjectName(lc, this, objectNameAsStr);
+ JMXConfigurator jmxConfigurator0 = new JMXConfigurator(lc, mbs, objectName);
+
listenerList = lc.getCopyOfListenerList();
- assertEquals(1, listenerList.size());
assertTrue(listenerList.contains(jmxConfigurator0));
- JMXConfigurator jmxConfigurator1 = MBeanUtil.register(lc, on, this);
+ JMXConfigurator jmxConfigurator1 = new JMXConfigurator(lc, mbs, objectName);
listenerList = lc.getCopyOfListenerList();
assertEquals(1, listenerList.size());
- assertFalse("old configurator should be absent", listenerList
+ assertTrue("old configurator should be present", listenerList
.contains(jmxConfigurator0));
- assertTrue("new configurator should be present", listenerList
+ assertFalse("new configurator should be absent", listenerList
.contains(jmxConfigurator1));
-
- // StatusPrinter.print(lc);
}
@Test
Modified: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/spi/BasicContextListener.java
==============================================================================
--- logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/spi/BasicContextListener.java (original)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/spi/BasicContextListener.java Wed Nov 12 16:26:28 2008
@@ -4,16 +4,33 @@
public class BasicContextListener implements LoggerContextListener {
- boolean updated = false;
+ enum UpdateType { NONE, START, RESET, STOP};
+
+ UpdateType updateType = UpdateType.NONE;
LoggerContext context;
+ boolean resetResistant;
+
+ public void setResetResistant(boolean resetResistant) {
+ this.resetResistant = resetResistant;
+ }
+
public void onReset(LoggerContext context) {
- updated = true;
+ updateType = UpdateType.RESET;
this.context = context;
}
public void onStart(LoggerContext context) {
- updated = true;
+ updateType = UpdateType.START;;
this.context = context;
}
+
+ public void onStop(LoggerContext context) {
+ updateType = UpdateType.STOP;;
+ this.context = context;
+ }
+
+ public boolean isResetResistant() {
+ return resetResistant;
+ }
}
Modified: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/spi/ContextListenerTest.java
==============================================================================
--- logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/spi/ContextListenerTest.java (original)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/spi/ContextListenerTest.java Wed Nov 12 16:26:28 2008
@@ -2,6 +2,7 @@
import junit.framework.TestCase;
import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.spi.BasicContextListener.UpdateType;
public class ContextListenerTest extends TestCase {
@@ -17,13 +18,27 @@
public void testNotifyOnReset() {
context.reset();
- assertTrue(listener.updated);
+ assertEquals(UpdateType.RESET, listener.updateType);
assertEquals(listener.context, context);
}
+
+ public void testNotifyOnStopResistant() {
+ listener.setResetResistant(true);
+ context.stop();
+ assertEquals(UpdateType.STOP, listener.updateType);
+ assertEquals(listener.context, context);
+ }
+
+ public void testNotifyOnStopNotResistant() {
+ context.stop();
+ assertEquals(UpdateType.RESET, listener.updateType);
+ assertEquals(listener.context, context);
+ }
+
public void testNotifyOnStart() {
context.start();
- assertTrue(listener.updated);
+ assertEquals(UpdateType.START, listener.updateType);
assertEquals(listener.context, context);
}
}
Modified: logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/util/InitializationTest.java
==============================================================================
--- logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/util/InitializationTest.java (original)
+++ logback/trunk/logback-classic/src/test/java/ch/qos/logback/classic/util/InitializationTest.java Wed Nov 12 16:26:28 2008
@@ -46,7 +46,7 @@
public void tearDown() throws Exception {
System.clearProperty(ContextInitializer.CONFIG_FILE_PROPERTY);
System.clearProperty(ContextInitializer.STATUS_LISTENER_CLASS);
- lc.reset();
+ lc.reset(); // we are going to need this context
}
@Test
@@ -73,7 +73,7 @@
assertTrue(appender instanceof ConsoleAppender);
}
{
- lc.reset();
+ lc.stop();
Appender appender = root.getAppender("STDOUT");
assertNull(appender);
}
@@ -88,7 +88,7 @@
}
public void doAutoConfigFromSystemProperties(String val) throws JoranException {
- //lc.shutdownAndReset();
+ //lc.reset();
System.setProperty(ContextInitializer.CONFIG_FILE_PROPERTY, val);
new ContextInitializer(lc).autoConfig();
Appender appender = root.getAppender("AUTO_BY_SYSTEM_PROPERTY");
Modified: logback/trunk/logback-examples/src/main/java/chapter4/ExitWoes2.java
==============================================================================
--- logback/trunk/logback-examples/src/main/java/chapter4/ExitWoes2.java (original)
+++ logback/trunk/logback-examples/src/main/java/chapter4/ExitWoes2.java Wed Nov 12 16:26:28 2008
@@ -42,7 +42,7 @@
logger.debug("Hello world.");
- lc.reset();
+ lc.stop();
StatusPrinter.print(lc);
}
Modified: logback/trunk/logback-examples/src/main/java/chapter4/socket/SocketClient2.java
==============================================================================
--- logback/trunk/logback-examples/src/main/java/chapter4/socket/SocketClient2.java (original)
+++ logback/trunk/logback-examples/src/main/java/chapter4/socket/SocketClient2.java Wed Nov 12 16:26:28 2008
@@ -45,7 +45,7 @@
if (configFile.endsWith(".xml")) {
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
JoranConfigurator configurator = new JoranConfigurator();
- lc.reset();
+ lc.stop();
configurator.setContext(lc);
configurator.doConfigure(configFile);
}
Modified: logback/trunk/logback-examples/src/main/java/chapter7/SimpleMDC.java
==============================================================================
--- logback/trunk/logback-examples/src/main/java/chapter7/SimpleMDC.java (original)
+++ logback/trunk/logback-examples/src/main/java/chapter7/SimpleMDC.java Wed Nov 12 16:26:28 2008
@@ -80,7 +80,7 @@
try {
JoranConfigurator configurator = new JoranConfigurator();
configurator.setContext(lc);
- lc.reset();
+ lc.stop();
URL url = Loader.getResourceBySelfClassLoader("chapter7/simpleMDC.xml");
configurator.doConfigure(url);
} catch (JoranException je) {
Modified: logback/trunk/logback-site/src/site/pages/manual/appenders.html
==============================================================================
--- logback/trunk/logback-site/src/site/pages/manual/appenders.html (original)
+++ logback/trunk/logback-site/src/site/pages/manual/appenders.html Wed Nov 12 16:26:28 2008
@@ -306,7 +306,7 @@
public static void main(String[] args) throws Exception {
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
- lc.shutdownAndReset(); // we want to override the default-config.
+ lc.reset(); // we want to override the default-config.
WriterAppender<LoggingEvent> writerAppender = new WriterAppender<LoggingEvent>();
writerAppender.setContext(lc);
writerAppender.setLayout(new EchoLayout<<LoggingEvent>());
@@ -335,7 +335,7 @@
the underlying output stream. As astonishing as this may seem,
running <code>ExitWoes1</code> will not produce any data in the file
<em>exitWoes1.log</em> because the Java VM does not flush output
- streams when it exits. Calling the <code>shutdownAndReset()</code>
+ streams when it exits. Calling the <code>stop()</code>
method of a <code>LoggerContext</code> ensures that all appenders in
the hierarchy are closed and their buffers are flushed. The <code><a
href="../xref/chapter4/ExitWoes2.html">ExitWoes2</a></code> class
@@ -1212,31 +1212,34 @@
lost due to server unavailability.
</p>
- <p>
- Even if a <code>SocketAppender</code> is no longer attached to any logger,
- it will not be garbage collected in the presence of a connector thread.
- A connector thread exists only if the connection to the server is down.
- To avoid this garbage collection problem, you should close the <code>SocketAppender</code>
- explicitly. Long lived applications which create/destroy many
- <code>SocketAppender</code> instances should be aware of this
- garbage collection problem. Most other applications can safely ignore it.
- If the JVM hosting the <code>SocketAppender</code> exits before the
- <code>SocketAppender</code> is closed, either explicitly or subsequent
- to garbage collection, then there might be untransmitted data in the
- pipe which may be lost. This is a common problem on Windows based systems.
- To avoid lost data, it is usually sufficient to <code>close()</code> the
- <code>SocketAppender</code> either explicitly or by calling the
- <code>LoggerContext</code>'s <code>shutdownAndReset()</code> method before exiting the application.
- </p>
-
- <p>
- The remote server is identified by the <span class="option">RemoteHost</span> and
- <span class="option">Port</span> options.
- <code>SocketAppender</code> options are listed in the following table.
+ <p>Even if a <code>SocketAppender</code> is no longer attached to
+ any logger, it will not be garbage collected in the presence of a
+ connector thread. A connector thread exists only if the
+ connection to the server is down. To avoid this garbage
+ collection problem, you should close the
+ <code>SocketAppender</code> explicitly. Long lived applications
+ which create/destroy many <code>SocketAppender</code> instances
+ should be aware of this garbage collection problem. Most other
+ applications can safely ignore it. If the JVM hosting the
+ <code>SocketAppender</code> exits before the
+ <code>SocketAppender</code> is closed, either explicitly or
+ subsequent to garbage collection, then there might be
+ untransmitted data in the pipe which may be lost. This is a common
+ problem on Windows based systems. To avoid lost data, it is
+ usually sufficient to <code>close()</code> the
+ <code>SocketAppender</code> either explicitly or by calling the
+ <code>LoggerContext</code>'s <code>stop()</code>
+ method before exiting the application.
+ </p>
+
+ <p>The remote server is identified by the <span
+ class="option">RemoteHost</span> and <span
+ class="option">Port</span> options. <code>SocketAppender</code>
+ options are listed in the following table.
</p>
- <table class="bodyTable">
- <tr class="a">
+ <table class="bodyTable">
+ <tr class="a">
<th>Option Name</th>
<th>Type</th>
<th>Description</th>
Modified: logback/trunk/logback-site/src/site/pages/manual/jmxConfig.html
==============================================================================
--- logback/trunk/logback-site/src/site/pages/manual/jmxConfig.html (original)
+++ logback/trunk/logback-site/src/site/pages/manual/jmxConfig.html Wed Nov 12 16:26:28 2008
@@ -12,9 +12,7 @@
<title>JMX Configuration</title>
</head>
<body>
- <script type="text/javascript">
- prefix='../';
- </script>
+ <script type="text/javascript">prefix='../';</script>
<script src="../templates/header.js" type="text/javascript"></script>
<div id="left">
<script src="../templates/left.js" type="text/javascript"></script>
@@ -46,7 +44,7 @@
your logback configuration file, as shown below:
</p>
-<div class="source"><pre><configuration>
+ <p class="source"><configuration>
<b><jmxConfigurator /></b>
<appender name="console" class="ch.qos.logback.classic.ConsoleAppender">
@@ -59,7 +57,7 @@
<level value="debug"/>
<appender-ref ref="console" />
</root>
-</configuration></pre></div>
+</configuration></p>
<p>After you connect to your server with <em>jconsole</em>, on the
MBeans panel, under "ch.qos.logback.classic.jmx.Configurator"
@@ -126,7 +124,7 @@
public void contextDestroyed(ServletContextEvent sce) {
<b>LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();</b>
- <b>lc.reset();</b>
+ <b>lc.stop();</b>
}
public void contextInitialized(ServletContextEvent sce) {
@@ -134,10 +132,6 @@
} </p>
-
-
-
-
<!-- ============ Multiple web-applications ================== -->
Modified: logback/trunk/logback-site/src/site/pages/manual/joran.html
==============================================================================
--- logback/trunk/logback-site/src/site/pages/manual/joran.html (original)
+++ logback/trunk/logback-site/src/site/pages/manual/joran.html Wed Nov 12 16:26:28 2008
@@ -383,7 +383,7 @@
configurator.setContext(lc);
// the context was probably already configured by default configuration
// rules
- lc.shutdownAndReset();
+ lc.reset();
configurator.doConfigure(args[0]);
} catch (JoranException je) {
je.printStackTrace();
Modified: logback/trunk/logback-site/src/site/pages/manual/mdc.html
==============================================================================
--- logback/trunk/logback-site/src/site/pages/manual/mdc.html (original)
+++ logback/trunk/logback-site/src/site/pages/manual/mdc.html Wed Nov 12 16:26:28 2008
@@ -365,7 +365,7 @@
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
JoranConfigurator configurator = new JoranConfigurator();
configurator.setContext(lc);
- lc.shutdownAndReset();
+ lc.reset();
configurator.doConfigure(args[0]);
} catch (JoranException je) {
je.printStackTrace();
Modified: logback/trunk/logback-site/src/site/pages/news.html
==============================================================================
--- logback/trunk/logback-site/src/site/pages/news.html (original)
+++ logback/trunk/logback-site/src/site/pages/news.html Wed Nov 12 16:26:28 2008
@@ -135,8 +135,8 @@
<p>The TurboFilterChain in a LoggerContext is <a
href="http://svn.qos.ch/viewvc?view=rev&revision=1678">now
- cleared</a> when the shutdownAndReset method is called. This problem
- was reported on May 1st, 2008, by Julia Hu on the logback
+ cleared</a> when the <code>reset</code>() method is called. This
+ problem was reported on May 1st, 2008, by Julia Hu on the logback
developpers list.
</p>
1
0

[JIRA] Commented: (LBCORE-18) classloader for loading autoConfig configuration resource
by Paul Nyheim (JIRA) 12 Nov '08
by Paul Nyheim (JIRA) 12 Nov '08
12 Nov '08
[ http://jira.qos.ch/browse/LBCORE-18?page=com.atlassian.jira.plugin.system.i… ]
Paul Nyheim commented on LBCORE-18:
-----------------------------------
What is the status of this blocking issue?
> classloader for loading autoConfig configuration resource
> ---------------------------------------------------------
>
> Key: LBCORE-18
> URL: http://jira.qos.ch/browse/LBCORE-18
> Project: logback-core
> Issue Type: Bug
> Components: Other
> Affects Versions: unspecified
> Environment: Operating System: Windows
> Platform: PC
> Reporter: Filip Jirsák
> Assignee: Logback dev list
> Priority: Blocker
> Attachments: classloader.patch
>
>
> ContextInitializer.autoConfig(LoggerContext) uses current thread context ClassLoader (TCL) nowadays - it calls Thread.currentThread().getContextClassLoader(). Sometimes this classloader has no access to logback classes - for example TCL is parent of classloader, which loaded current class, in oc4j (Oracle J2EE container) at the time of starting servlet container. So logback-*.jar are in WEB-INF/lib directory, but TCL has no access to these jars and to files in WEB-INF/classes.
> I think ContextInitializer.autoConfig(LoggerContext) should try this.getClass().getClassLoader() first and only if it fails it should use Thread.currentThread().getContextClassLoader().
--
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators: http://jira.qos.ch/secure/Administrators.jspa
-
For more information on JIRA, see: http://www.atlassian.com/software/jira
1
0

[JIRA] Created: (LBCLASSIC-84) Backslashes (Windows) cause issues with rolling
by Mike Boyers (JIRA) 12 Nov '08
by Mike Boyers (JIRA) 12 Nov '08
12 Nov '08
Backslashes (Windows) cause issues with rolling
-----------------------------------------------
Key: LBCLASSIC-84
URL: http://jira.qos.ch/browse/LBCLASSIC-84
Project: logback-classic
Issue Type: Bug
Affects Versions: unspecified
Environment: Windows
Reporter: Mike Boyers
Assignee: Logback dev list
This may be a duplicate of LBCLASSIC-56, but I wasn't sure, so I went ahead and created this issue.
On windows, my logs failed to roll when I used a "windows-looking" absolute path when calling the rolling policy's setFileNamePattern() method. If I use forward slashes instead of backwards slashes, it works fine.
--
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators: http://jira.qos.ch/secure/Administrators.jspa
-
For more information on JIRA, see: http://www.atlassian.com/software/jira
2
5