[slf4j-dev] svn commit: r1079 - in slf4j/trunk: slf4j-ext/src/main/java/org/slf4j/profiler slf4j-ext/src/test/java/org/slf4j/profiler slf4j-ext/src/test/resources slf4j-site/src/site/pages
ceki at slf4j.org
ceki at slf4j.org
Mon Jul 28 22:57:42 CEST 2008
Author: ceki
Date: Mon Jul 28 22:57:41 2008
New Revision: 1079
Added:
slf4j/trunk/slf4j-ext/src/test/java/org/slf4j/profiler/BasicProfilerDemo.java
slf4j/trunk/slf4j-ext/src/test/java/org/slf4j/profiler/NestedProfilerDemo.java
- copied, changed from r1075, /slf4j/trunk/slf4j-ext/src/test/java/org/slf4j/profiler/ProfilerDemo.java
slf4j/trunk/slf4j-ext/src/test/java/org/slf4j/profiler/NestedProfilerDemo2.java
slf4j/trunk/slf4j-ext/src/test/resources/
slf4j/trunk/slf4j-ext/src/test/resources/log4j.properties
slf4j/trunk/slf4j-site/src/site/pages/support.html
Removed:
slf4j/trunk/slf4j-ext/src/test/java/org/slf4j/profiler/ProfilerDemo.java
Modified:
slf4j/trunk/slf4j-ext/src/main/java/org/slf4j/profiler/Profiler.java
slf4j/trunk/slf4j-ext/src/main/java/org/slf4j/profiler/StopWatch.java
slf4j/trunk/slf4j-ext/src/main/java/org/slf4j/profiler/TimeInstrument.java
slf4j/trunk/slf4j-ext/src/test/java/org/slf4j/profiler/SortAndPruneComposites.java
slf4j/trunk/slf4j-site/src/site/pages/extensions.html
slf4j/trunk/slf4j-site/src/site/pages/index.html
Log:
- improved Profiler docs and other minor changes
Modified: slf4j/trunk/slf4j-ext/src/main/java/org/slf4j/profiler/Profiler.java
==============================================================================
--- slf4j/trunk/slf4j-ext/src/main/java/org/slf4j/profiler/Profiler.java (original)
+++ slf4j/trunk/slf4j-ext/src/main/java/org/slf4j/profiler/Profiler.java Mon Jul 28 22:57:41 2008
@@ -30,7 +30,6 @@
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;
-
// + Profiler [BAS]
// |-- elapsed time [doX] 0 milliseconds.
// |-- elapsed time [doYYYYY] 56 milliseconds.
@@ -192,6 +191,9 @@
public void log() {
Marker profilerMarker = MarkerFactory.getMarker(PROFILER_MARKER_NAME);
+ if(logger == null) {
+ throw new NullPointerException("If you invoke the log() method, then you must associate a logger with this profiler.");
+ }
if (logger.isDebugEnabled(profilerMarker)) {
DurationUnit du = Util.selectDurationUnitForDisplay(globalStopWatch);
String r = buildProfilerString(du, TOP_PROFILER_FIRST_PREFIX, TOTAL_ELAPSED, "");
@@ -238,20 +240,4 @@
buf.append(SpacePadder.LINE_SEP);
}
- static void XXXbuildStringForGlobalStopWatch(StringBuffer buf,
- String indentation, StopWatch sw, DurationUnit du) {
- buf.append(indentation);
- buf.append("|--");
- //buf.append(prefix);
- //buf.append(" Total elapsed time ");
- SpacePadder.leftPad(buf, "[" + sw.getName() + "]", MIN_SW_NAME_LENGTH);
- buf.append(" ");
- String timeStr = Util.durationInDunrationUnitsAsStr(sw.elapsedTime(),
- du);
- SpacePadder.leftPad(buf, timeStr, MIN_SW_ELAPSED_TIME_NUMBER_LENGTH);
- buf.append(" ");
- Util.appendDurationUnitAsStr(buf, du);
- buf.append(SpacePadder.LINE_SEP);
- }
-
}
Modified: slf4j/trunk/slf4j-ext/src/main/java/org/slf4j/profiler/StopWatch.java
==============================================================================
--- slf4j/trunk/slf4j-ext/src/main/java/org/slf4j/profiler/StopWatch.java (original)
+++ slf4j/trunk/slf4j-ext/src/main/java/org/slf4j/profiler/StopWatch.java Mon Jul 28 22:57:41 2008
@@ -96,4 +96,8 @@
System.out.println(toString());
}
+ public void log() {
+ throw new UnsupportedOperationException("A stopwatch instance does not know how to log");
+ }
+
}
Modified: slf4j/trunk/slf4j-ext/src/main/java/org/slf4j/profiler/TimeInstrument.java
==============================================================================
--- slf4j/trunk/slf4j-ext/src/main/java/org/slf4j/profiler/TimeInstrument.java (original)
+++ slf4j/trunk/slf4j-ext/src/main/java/org/slf4j/profiler/TimeInstrument.java Mon Jul 28 22:57:41 2008
@@ -21,4 +21,6 @@
long elapsedTime();
void print();
+
+ void log();
}
Added: slf4j/trunk/slf4j-ext/src/test/java/org/slf4j/profiler/BasicProfilerDemo.java
==============================================================================
--- (empty file)
+++ slf4j/trunk/slf4j-ext/src/test/java/org/slf4j/profiler/BasicProfilerDemo.java Mon Jul 28 22:57:41 2008
@@ -0,0 +1,63 @@
+package org.slf4j.profiler;
+
+/**
+ *
+ * This demo illustrates usage of SLF4J profilers.
+ *
+ * <p>
+ * We have been given the task of generating a large number, say N, of random
+ * integers. We need to transform that array into a smaller array containing
+ * only prime numbers. The new array has to be sorted.
+ *
+ * <p>
+ * While tackling this problem, we would like to measure the time spent in each
+ * subtask.
+ *
+ * <p>
+ * A typical output for this demo would be:
+ *
+ * <pre>
+ + Profiler [BASIC]
+ |-- elapsed time [A] 213.186 milliseconds.
+ |-- elapsed time [B] 2499.107 milliseconds.
+ |-- elapsed time [OTHER] 3300.752 milliseconds.
+ |-- Total [BASIC] 6014.161 milliseconds.
+ </pre>
+ *
+ * @author Ceki Gulcu
+ */
+public class BasicProfilerDemo {
+
+ public static void main(String[] args) {
+ // create a profiler called "BASIC"
+ Profiler profiler = new Profiler("BASIC");
+ profiler.start("A");
+ doA();
+
+ profiler.start("B");
+ doB();
+
+ profiler.start("OTHER");
+ doOther();
+ profiler.stop().print();
+ }
+
+ static private void doA() {
+ delay(200);
+ }
+
+ static private void doB() {
+ delay(2500);
+ }
+
+ static private void doOther() {
+ delay(3300);
+ }
+
+ static private void delay(int millis) {
+ try {
+ Thread.sleep(millis);
+ } catch (InterruptedException e) {
+ }
+ }
+}
Copied: slf4j/trunk/slf4j-ext/src/test/java/org/slf4j/profiler/NestedProfilerDemo.java (from r1075, /slf4j/trunk/slf4j-ext/src/test/java/org/slf4j/profiler/ProfilerDemo.java)
==============================================================================
--- /slf4j/trunk/slf4j-ext/src/test/java/org/slf4j/profiler/ProfilerDemo.java (original)
+++ slf4j/trunk/slf4j-ext/src/test/java/org/slf4j/profiler/NestedProfilerDemo.java Mon Jul 28 22:57:41 2008
@@ -30,21 +30,33 @@
*
* @author Ceki Gulcu
*/
-public class ProfilerDemo {
+public class NestedProfilerDemo {
public static void main(String[] args) {
+ // create a profiler called "DEMO"
Profiler profiler = new Profiler("DEMO");
+
+ // register this profiler in the thread context's profiler registry
ProfilerRegistry profilerRegistry = ProfilerRegistry.getThreadContextInstance();
profiler.registerWith(profilerRegistry);
+ // start a stopwatch called "RANDOM"
profiler.start("RANDOM");
- RandomIntegerArrayGenerator riag = new RandomIntegerArrayGenerator();
- int n = 100*1000;
- int[] randomArray = riag.generate(n);
+ RandomIntegerArrayGenerator riaGenerator = new RandomIntegerArrayGenerator();
+ int n = 10*1000;
+ int[] randomArray = riaGenerator.generate(n);
+ // create and start a nested profiler called "SORT_AND_PRUNE"
+ // By virtue of its parent-child relationship with the "DEMO"
+ // profiler, and the previous registration of the parent profiler,
+ // this nested profiler will be automatically registered
+ // with the thread context's profiler registry
profiler.startNested(SortAndPruneComposites.NESTED_PROFILER_NAME);
+
SortAndPruneComposites pruner = new SortAndPruneComposites(randomArray);
pruner.sortAndPruneComposites();
+
+ // stop and print the "DEMO" printer
profiler.stop().print();
}
}
Added: slf4j/trunk/slf4j-ext/src/test/java/org/slf4j/profiler/NestedProfilerDemo2.java
==============================================================================
--- (empty file)
+++ slf4j/trunk/slf4j-ext/src/test/java/org/slf4j/profiler/NestedProfilerDemo2.java Mon Jul 28 22:57:41 2008
@@ -0,0 +1,42 @@
+package org.slf4j.profiler;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+
+/**
+ *
+ * This demo illustrates usage of SLF4J profilers. It is almost identical to
+ * the first NestProfilerDemo, except that it uses a logger instead of
+ * printing its output on the console.
+ *
+
+ * @author Ceki Gulcu
+ */
+public class NestedProfilerDemo2 {
+
+ static Logger logger = LoggerFactory.getLogger(NestedProfilerDemo2.class);
+
+ public static void main(String[] args) {
+ Profiler profiler = new Profiler("DEMO");
+ // associate a logger with the profiler
+ profiler.setLogger(logger);
+
+ ProfilerRegistry profilerRegistry = ProfilerRegistry.getThreadContextInstance();
+ profiler.registerWith(profilerRegistry);
+
+ profiler.start("RANDOM");
+ RandomIntegerArrayGenerator riaGenerator = new RandomIntegerArrayGenerator();
+ int n = 10*1000;
+ int[] randomArray = riaGenerator.generate(n);
+
+ profiler.startNested(SortAndPruneComposites.NESTED_PROFILER_NAME);
+
+ SortAndPruneComposites pruner = new SortAndPruneComposites(randomArray);
+ pruner.sortAndPruneComposites();
+
+ // stop and log
+ profiler.stop().log();
+ }
+}
Modified: slf4j/trunk/slf4j-ext/src/test/java/org/slf4j/profiler/SortAndPruneComposites.java
==============================================================================
--- slf4j/trunk/slf4j-ext/src/test/java/org/slf4j/profiler/SortAndPruneComposites.java (original)
+++ slf4j/trunk/slf4j-ext/src/test/java/org/slf4j/profiler/SortAndPruneComposites.java Mon Jul 28 22:57:41 2008
@@ -17,11 +17,14 @@
}
public int[] sortAndPruneComposites() {
-
+ // retrieve previously registered profiler named "SORT_AND_PRUNE"
ProfilerRegistry profilerRegistry = ProfilerRegistry.getThreadContextInstance();
Profiler sortProfiler = profilerRegistry.get(NESTED_PROFILER_NAME);
+
+ // start a new stopwatch called SORT
sortProfiler.start("SORT");
int[] sortedArray = sort();
+ // start a new stopwatch called PRUNE_COMPOSITES
sortProfiler.start("PRUNE_COMPOSITES");
int result[] = pruneComposites(sortedArray);
Added: slf4j/trunk/slf4j-ext/src/test/resources/log4j.properties
==============================================================================
--- (empty file)
+++ slf4j/trunk/slf4j-ext/src/test/resources/log4j.properties Mon Jul 28 22:57:41 2008
@@ -0,0 +1,6 @@
+
+log4j.rootLogger=DEBUG, CONSOLE
+
+log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
+log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
+log4j.appender.CONSOLE.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
Modified: slf4j/trunk/slf4j-site/src/site/pages/extensions.html
==============================================================================
--- slf4j/trunk/slf4j-site/src/site/pages/extensions.html (original)
+++ slf4j/trunk/slf4j-site/src/site/pages/extensions.html Mon Jul 28 22:57:41 2008
@@ -48,45 +48,43 @@
<h2>Basic example</h2>
- <em>Example: Using the profiler </em>
+ <em>Example: Using the profiler: <a
+ href="xref-test/org/slf4j/profiler/BasicProfilerDemo.html">BasicProfilerDemo</a></em>
- <p class="source">
+ <p class="source">[omitted]
+32 public class BasicProfilerDemo {
+33
+34 public static void main(String[] args) {
+35 // create a profiler called "BASIC"
+36 <b>Profiler profiler = new Profiler("BASIC");</b>
+37 <b>profiler.start("A");</b>
+38 doA();
+39
+40 <b>profiler.start("B");</b>
+41 doB();
+42
+43 <b>profiler.start("OTHER");</b>
+44 doOther();
+45 <b>profiler.stop().print();</b>
+46 }
+[omitted]</p>
-import ch.qos.logback.classic.stopwatch.Profiler;
-
-public class BasicUsageExample {
-
- public static void main(String[] args) {
- <b>Profiler profiler = new Profiler("BASIC");</b>
- <b>profiler.start("A");</b>
- doA();
-
- <b>profiler.start("B");</b>
- for (int i = 0; i < 5; i++) {
- doSubtaskTwo(i);
- }
- <b>profiler.start("doOther");</b>
- doOther();
- System.out.println(<b>profiler.stop().toString()</b>);
- }
- ... cut </p>
<p>Running the above example will output the following output.</p>
<p class="source">+ Profiler [BASIC]
-|-- elapsed time [A] 0.288 milliseconds.
-|-- elapsed time [B] 24.717 milliseconds.
-|-- elapsed time [Other] 22.085 milliseconds.
-|-- Total elapsed time [BASIC] 50.691 milliseconds.</p>
-
+|-- elapsed time [A] 220.487 milliseconds.
+|-- elapsed time [B] 2499.866 milliseconds.
+|-- elapsed time [OTHER] 3300.745 milliseconds.
+|-- Total [BASIC] 6022.568 milliseconds.</p>
<p>Instantiating a profiler starts a global stopwatch. Each call to
the start() method starts a new and named stopwatch. In addition to
- sarting a named stopwatch, the start() method also causes the
+ starting a named stopwatch, the start() method also causes the
previous stopwatch to stop. Thus, the call to
<code>profiler.start("A")</code> starts a stopwatch named "A". The
subsequent call to <code>profiler.start("B")</code> starts
- stopwatch "B" and simultanously stops the stopwatch named
+ stopwatch "B" and simultaneously stops the stopwatch named
"A". Invoking the <code>stop()</code> on a profiler method stops
the last stopwatch as well as the global stopwatch which was
started when the profiler was instantiated.
@@ -106,12 +104,191 @@
<p>Often times, the subtask is implemented by a different class as
the class hosting the parent profiler. Using the
- <code>ProfilerRegistry</code> is a convinient way of passing a
- nested profiler to an object outside the current object.
+ <code>ProfilerRegistry</code> is a convenient way of passing a
+ nested profiler to an object outside the current object. Each
+ thread has its own profiler registry which can be retrieved by
+ invoking the <code>getThreadContextInstance()</code> method.
</p>
<em>Example: <a
- href="xref-test/org/slf4j/profiler/ProfilerDemo.html">ProfilerDemo</a>
+ href="xref-test/org/slf4j/profiler/NestedProfilerDemo.html">NestedProfilerDemo</a>
+ </em>
+
+ <p class="source">33 public class NestedProfilerDemo {
+34
+35 public static void main(String[] args) {
+36 // create a profiler called "DEMO"
+37 Profiler profiler = new Profiler("DEMO");
+38
+39 // register this profiler in the thread context's profiler registry
+40 <b>ProfilerRegistry profilerRegistry = ProfilerRegistry.getThreadContextInstance();</b>
+41 <b>profiler.registerWith(profilerRegistry);</b>
+42
+43 // start a stopwatch called "RANDOM"
+44 profiler.start("RANDOM");
+45 RandomIntegerArrayGenerator riaGenerator = new RandomIntegerArrayGenerator();
+46 int n = 1000*1000;
+47 int[] randomArray = riaGenerator.generate(n);
+48
+49 // create and start a nested profiler called "SORT_AND_PRUNE"
+50 // By virtue of its parent-child relationship with the "DEMO"
+51 // profiler, and the previous registration of the parent profiler,
+52 // this nested profiler will be automatically registered
+53 // with the thread context's profiler registry
+54 <b>profiler.startNested(SortAndPruneComposites.NESTED_PROFILER_NAME);</b>
+55
+56 SortAndPruneComposites pruner = new SortAndPruneComposites(randomArray);
+57 pruner.sortAndPruneComposites();
+58
+59 // stop and print the "DEMO" printer
+60 profiler.stop().print();
+61 }
+62 }</p>
+
+ <p>Here is the relevant excerpt from the <a
+ href="xref-test/org/slf4j/profiler/SortAndPruneComposites.html">SortAndPruneComposites</a>
+ class.
+ </p>
+
+ <p class="source">[omitted]
+6 public class SortAndPruneComposites {
+7
+8 static String NESTED_PROFILER_NAME = "SORT_AND_PRUNE";
+9
+10 final int[] originalArray;
+11 final int originalArrrayLength;
+12
+13 public SortAndPruneComposites(int[] randomArray) {
+14 this.originalArray = randomArray;
+15 this.originalArrrayLength = randomArray.length;
+16
+17 }
+18
+19 public int[] sortAndPruneComposites() {
+20 // retrieve previously registered profiler named "SORT_AND_PRUNE"
+21 ProfilerRegistry profilerRegistry = ProfilerRegistry.getThreadContextInstance();
+22 <b>Profiler sortProfiler = profilerRegistry.get(NESTED_PROFILER_NAME);</b>
+23
+24 // start a new stopwatch called SORT
+25 sortProfiler.start("SORT");
+26 int[] sortedArray = sort();
+27 // start a new stopwatch called PRUNE_COMPOSITES
+28 sortProfiler.start("PRUNE_COMPOSITES");
+29 int result[] = pruneComposites(sortedArray);
+30
+31 return result;
+32 }
+[omitted] </p>
+
+
+ <p>On a Dual-Core Intel CPU clocked at 3.2 GHz, running the
+ <code>ProfilerDemo</code> application yields the following output:</p>
+
+ <p class="source">+ Profiler [DEMO]
+|-- elapsed time [RANDOM] 70.524 milliseconds.
+|---+ Profiler [SORT_AND_PRUNE]
+ |-- elapsed time [SORT] 665.281 milliseconds.
+ |-- elapsed time [PRUNE_COMPOSITES] 5695.515 milliseconds.
+ |-- Subtotal [SORT_AND_PRUNE] 6360.866 milliseconds.
+|-- elapsed time [SORT_AND_PRUNE] 6360.866 milliseconds.
+|-- Total [DEMO] 6433.922 milliseconds.</p>
+
+ <p>From the above, we learn that generating 1'000'000 random
+ integers takes 70 ms, sorting them 665 ms, and pruning the composite
+ (non-prime) integers 5695 ms, for a grand total of 6433 ms. Given
+ that pruning composites takes most of the CPU effort, any future
+ optimizations efforts would be directed at the pruning part.
+ </p>
+
+ <p>With just a few well-placed profiler calls we were able to
+ identify hot-spots in our application. Also note that passing a
+ profiler to a target class could be achieved by registering it in a
+ profiler registry and then retrieving it in the target class.
+ </p>
+
+ <h2>Printing using a logger</h2>
+
+ <p> Invoking <code>profiler.print</code> will always print the
+ output on the console. If you wish to leave the profiler code in
+ production, then you probably need more control over the output
+ destination.
+ </p>
+
+ <p>To use a logger, you need to associate a logger of your choice
+ with the profiler and then invoke the <code>log()</code> method
+ instead of <code>print()</code>, as the next example illustrates.
+ </p>
+
+ <em>Profiler with a logger: <a
+ href="xref-test/org/slf4j/profiler/NestedProfilerDemo2.html">NestedProfilerDemo2</a>
+ </em>
+
+ <p class="source">[omitted]
+17 public class NestedProfilerDemo2 {
+18
+19 static Logger logger = LoggerFactory.getLogger(NestedProfilerDemo2.class);
+20
+21 public static void main(String[] args) {
+22 Profiler profiler = new Profiler("DEMO");
+23 // associate a logger with the profiler
+24 <b>profiler.setLogger(logger);</b>
+25
+26 ProfilerRegistry profilerRegistry = ProfilerRegistry.getThreadContextInstance();
+27 profiler.registerWith(profilerRegistry);
+28
+29 profiler.start("RANDOM");
+30 RandomIntegerArrayGenerator riaGenerator = new RandomIntegerArrayGenerator();
+31 int n = 10*1000;
+32 int[] randomArray = riaGenerator.generate(n);
+33
+34 profiler.startNested(SortAndPruneComposites.NESTED_PROFILER_NAME);
+35
+36 SortAndPruneComposites pruner = new SortAndPruneComposites(randomArray);
+37 pruner.sortAndPruneComposites();
+38
+39 // stop and log
+40 profiler.stop().<b>log()</b>;
+41 }
+42 } </p>
+
+ <p>The output generated by this example will depend on the logging
+ environment, but should be very similar to the output generated by
+ the previous <code>NestedProfilerDemo</code> example.
+ </p>
+
+ <p>The log() method logs at level DEBUG using a marker named
+ "PROFILER".</p>
+
+ <p>If your logging system supports markers, e.g. logback, you could
+ specifically enable or disable output generated by SLF4J
+ profilers. Here is logback configuration file disabling output for
+ any logging event bearing the "PROFILER" marker, even if the logger
+ used by the profiler is enabled for the debug level.
+ </p>
+
+
+ <em>logback configuration disabling logging from profilers, and only
+ profilers</em>
+
+ <p class="source"><configuration>
+
+ <b><turboFilter class="ch.qos.logback.classic.turbo.MarkerFilter">
+ <Marker>PROFILER</Marker>
+ <OnMatch>DENY</OnMatch>
+ </turboFilter></b>
+
+ <appender name="STDOUT"
+ class="ch.qos.logback.core.ConsoleAppender">
+ <layout class="ch.qos.logback.classic.PatternLayout">
+ <Pattern>%-5level %logger{36} - %msg%n</Pattern>
+ </layout>
+ </appender>
+
+ <root>
+ <level value="DEBUG" />
+ <appender-ref ref="STDOUT" />
+ </root>
+</configuration> </p>
</div>
</body>
Modified: slf4j/trunk/slf4j-site/src/site/pages/index.html
==============================================================================
--- slf4j/trunk/slf4j-site/src/site/pages/index.html (original)
+++ slf4j/trunk/slf4j-site/src/site/pages/index.html Mon Jul 28 22:57:41 2008
@@ -108,6 +108,7 @@
<li><a href="http://displaytag.sourceforge.net/11/">Display tag</a></li>
<li><a href="http://groovy.codehaus.org/GMaven">GMaven</a></li>
<li><a href="http://www.h2database.com/">H2 Database</a></li>
+ <li><a href="http://www.gradle.org/">Gradle</a></li>
<li><a href="http://www.icegreen.com/greenmail/">GreenMail</a></li>
<li><a href="http://ha-jdbc.sourceforge.net/">HA-JDBC</a></li>
<li><a href="http://www.hibernate.org/">Hibernate</a></li>
Added: slf4j/trunk/slf4j-site/src/site/pages/support.html
==============================================================================
--- (empty file)
+++ slf4j/trunk/slf4j-site/src/site/pages/support.html Mon Jul 28 22:57:41 2008
@@ -0,0 +1,43 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
+<title>Log4j Bridge</title>
+<link rel="stylesheet" type="text/css" media="screen" href="css/site.css" />
+<link rel="stylesheet" type="text/css" media="print" href="css/print.css" />
+
+</head>
+<body>
+ <script>
+prefix='';
+</script>
+
+<script src="templates/header.js"></script>
+<div id="left">
+ <script src="templates/left.js"></script>
+</div>
+<div id="right">
+ <script src="templates/right.js"></script>
+</div>
+<div id="content">
+
+ <h2>Contractual Support</h2>
+
+
+ <p>The following companies, listed in alphabetical order, offer
+ contractual support for SLF4J.
+ </p>
+
+ <ul>
+ <li>QOS.ch, in Lausanne, Swizerland. For more information visit
+ QOS.ch's <a href="">support page</a>. </li>
+ </ul>
+
+
+
+<script src="templates/footer.js"></script>
+</div>
+</body>
+</html>
+
+
More information about the slf4j-dev
mailing list