Index: src/java/org/apache/log4j/concurrent/SynchronizedBoolean.java
===================================================================
--- src/java/org/apache/log4j/concurrent/SynchronizedBoolean.java (revision 0)
+++ src/java/org/apache/log4j/concurrent/SynchronizedBoolean.java (revision 0)
@@ -0,0 +1,54 @@
+
+/*
+ File: SynchronizedBoolean.java
+
+ Originally written by Doug Lea and released into the public domain.
+ This may be used for any purposes whatsoever without acknowledgment.
+ Thanks for the assistance and support of Sun Microsystems Labs,
+ and everyone contributing, testing, and using this code.
+
+ History:
+ Date Who What
+ 19Jun1998 dl Create public version
+*/
+
+package org.apache.log4j.concurrent;
+
+/**
+ * A class useful for offloading synch for boolean instance variables.
+ * A cut down version of the original Doug Lea class.
+ */
+public final class SynchronizedBoolean {
+
+ private boolean value;
+
+ /**
+ * Make a new SynchronizedBoolean with the given initial value,
+ * and using its own internal lock.
+ **/
+ public SynchronizedBoolean(boolean initialValue) {
+ value = initialValue;
+ }
+
+ /**
+ * Return the current value
+ **/
+ public synchronized boolean get() { return value; }
+
+ /**
+ * Set to newValue.
+ * @return the old value
+ **/
+ public synchronized boolean set(boolean newValue) {
+ boolean old = value;
+ value = newValue;
+ return old;
+ }
+
+ /**
+ * Returns String.valueOf(get()))
.
+ */
+ public String toString() { return String.valueOf(get()); }
+
+}
+
Index: src/java/org/apache/log4j/concurrent/ConcurrentAppender.java
===================================================================
--- src/java/org/apache/log4j/concurrent/ConcurrentAppender.java (revision 0)
+++ src/java/org/apache/log4j/concurrent/ConcurrentAppender.java (revision 0)
@@ -0,0 +1,429 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.concurrent;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.Appender;
+import org.apache.log4j.Priority;
+import org.apache.log4j.helpers.ReaderWriterLock;
+import org.apache.log4j.spi.ComponentBase;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.OptionHandler;
+
+/**
+ * Base class for appenders that can benefit from a concurrency strategy.
+ * Classes derived from this appender may have the {@link #append} method
+ * called by multiple threads. Derived classes must also override {@link
+ * #internalClose}.
+ *
+ * Locking strategy: Internally, there is a read-write lock to handle + * concurrent modification. A write lock is obtained to change states + * (including before {@link #close}.) A read lock is obtained to read + * options. Subclasses interested in state may check state using a public + * method or within their own {@link #append} method. + *
+ *+ * This class is heavily based on the {@link + * #org.apache.log4j.AppenderSkeleton} class. It may be a useful base class + * for creating appenders that can benefit from concurrent I/O access. + *
+ * + * @see #getWriteLock + */ +public abstract class ConcurrentAppender + extends ComponentBase implements Appender, OptionHandler +{ + + /** + * The layout variable does not need to be set if the appender + * implementation has its own layout. + */ + private Layout layout; + + /** + * The name of this appender. + */ + protected String name; + + /** + * There is no level threshold filtering by default. + */ + private volatile Priority threshold; + + /** + * Internal class, internally locked. + */ + private FilterChain filters = new FilterChain(); + + /** + * Is this appender closed? + */ + private SynchronizedBoolean closed = new SynchronizedBoolean(false); + + /** + * Set to true when the appender is activated. + * Subclasses can set this to false to indicate things are not in order. + */ + protected SynchronizedBoolean active = new SynchronizedBoolean(false); + + /** + * The guard prevents an appender from repeatedly calling its own doAppend + * method. This prevents same-thread re-entry looping. + */ + private ThreadLocal guard = new ThreadLocal(); + + /** + * A write lock is obtained to change options, a read lock is obtained to + * append events. + */ + private ReaderWriterLock lock = new ReaderWriterLock(); + + + /** + * Constructs a ConcurrentAppender. + * + * @param isActive true if appender is ready for use upon construction. + */ + protected ConcurrentAppender(final boolean isActive) { + active.set(isActive); + } + + /** + * Derived appenders should override this method if option structure + * requires it. + * By default, sets {@link #active} to true. + */ + public void activateOptions() { + active.set(true); + } + + /** + * Indicates if the appender is active and not closed. + */ + public boolean isActive() { + return active.get() && !closed.get(); + } + + /** + * Adds a filter to end of the filter list. + * @param filter filter to use; cannot be null + */ + public void addFilter(Filter filter) { + filters.addFilter(filter); + } + + /** + * Clears the filters chain. + */ + public void clearFilters() { + filters.clear(); + } + + /** + * Returns the first {@link Filter}. + */ + public Filter getFilter() { + return filters.getHead(); + } + + /** + * Returns the layout of this appender. May return null if not set. + */ + public Layout getLayout() { + return this.layout; + } + + /** + * Returns the name of this appender. + */ + public final String getName() { + return this.name; + } + + /** + * Returns this appender's threshold level. See the {@link #setThreshold} + * method for the meaning of this option. + */ + public Priority getThreshold() { + return threshold; + } + + /** + * Returns true if the message level is below the appender's threshold. If + * there is no threshold set, returnstrue
.
+ */
+ public boolean isAsSevereAsThreshold(final Priority level) {
+ Priority copy = threshold;
+ return ((copy == null) || copy.isGreaterOrEqual(level));
+ }
+
+ /**
+ * Performs threshold checks and checks filters before delegating actual
+ * logging to the subclasses specific {@link #append} method.
+ * This implementation also checks if this thread already is logging using
+ * this appender, preventing possible stack overflow.
+ */
+ public final void doAppend(LoggingEvent event) {
+
+ if (!isAsSevereAsThreshold(event.getLevel()))
+ return;
+
+ if (!filters.accept(event))
+ return;
+
+ // Prevent concurrent re-entry by this thread
+ // (There might be a cheaper way to do this)
+ // (Or maybe this lock is not necessary)
+ if (guard.get() != null)
+ return;
+
+ guard.set(this); // arbitrary thread lock object
+ try {
+
+ lock.getReadLock();
+ try {
+
+
+ if (closed.get()) {
+ getNonFloodingLogger().error(
+ "Attempted to use closed appender named [" + name + "].");
+ return;
+ }
+
+ if (!active.get()) {
+ getNonFloodingLogger().error(
+ "Attempted to log with inactive named [" + name + "].");
+ return;
+ }
+
+ append(event);
+
+ } finally {
+ lock.releaseReadLock();
+ }
+
+ } finally {
+ guard.set(null);
+ }
+ }
+
+ /**
+ * Sets the layout for this appender. Note that some appenders have their own
+ * (fixed) layouts or do not use one. For example, the {@link
+ * org.apache.log4j.net.SocketAppender} ignores the layout set here.
+ * + * Note that the implementation of {@link Layout} must be thread-safe. + * Common layouts such as {@link org.apache.log4j.PatternLayout} are + * thread-safe. + *
+ * + * @param layout new layout to use; may be null + */ + public void setLayout(Layout layout) { + this.layout = layout; + } + + /** + * Sets the name of this Appender. + */ + public void setName(String name) { + this.name = name; + } + + /** + * Sets the threshold level. + * All log events with lower level than the threshold level are ignored by + * the appender. + * + * @param threshold new threshold; may be null + */ + public void setThreshold(final Priority threshold) { + this.threshold = threshold; + } + + /** + * Returns true if this appender is closed. + * An appender, once closed, is closed forever. + */ + public boolean getClosed() { + return closed.get(); + } + + /** + * Cleans up this appender. + * Marked asfinal
to prevent subclasses from accidentally
+ * overriding and forgetting to call super.close()
or obtain a
+ * write lock.
+ * Calls {@link #internalClose} when completed.
+ * Implementation note: Obtains a write lock before starting close.
+ * Calling this method more than once does nothing.
+ */
+ public final void close() {
+ boolean wasClosed;
+ getWriteLock();
+ try {
+ wasClosed = closed.set(true);
+ } finally {
+ lock.releaseWriteLock();
+ }
+
+ if (!wasClosed)
+ internalClose();
+ }
+
+ /**
+ * Called to check if the appender is closed.
+ */
+ public boolean isClosed() {
+ return closed.get();
+ }
+
+ public final org.apache.log4j.spi.ErrorHandler getErrorHandler() {
+ return org.apache.log4j.helpers.OnlyOnceErrorHandler.INSTANCE;
+ }
+
+ public final void setErrorHandler(org.apache.log4j.spi.ErrorHandler eh) {
+ }
+
+ /**
+ * Returns a string representation of this object.
+ */
+ public String toString() {
+ return super.toString() + " name=" + name +
+ " threshold=" + threshold +
+ " layout=" + layout +
+ " filters=" + filters;
+ }
+
+ // PROTECTED METHODS
+
+ /**
+ * Subclasses of ConcurrentAppender
should implement this method
+ * to perform actual logging.
+ * This object holds a read lock during this method. This method may be
+ * called simultaneously by multiple threads.
+ */
+ protected abstract void append(LoggingEvent event);
+
+ /**
+ * Subclasses must implement their own close routines.
+ * This method is called by the {@link #close} method.
+ * This is guaranteed to be called only once, even if {@link #close} is
+ * invoked more than once.
+ * Note that further locking is not required, as {@link #append} can no
+ * longer be called.
+ */
+ protected abstract void internalClose();
+
+ /**
+ * Obtains a write lock that blocks logging to {@link #append}.
+ * This is normally done when changing output behavior, closing and reopening
+ * streams, etc. Call {@link #releaseWriteLock} to resume logging.
+ * + * Usage pattern: +
+ getWriteLock(); + try { + // ... + } finally { + releaseWriteLock(); + } ++ * Note: Do not attempt to re-acquire this lock. This lock should only be + * used for critical sections and not for long periods of time. + */ + protected void getWriteLock() { + lock.getWriteLock(); + } + + /** + * Releases a write lock; allows calls to the {@link #append} method. + * @see #getWriteLock + */ + protected void releaseWriteLock() { + lock.releaseWriteLock(); + } + + /** + * Finalizes this appender by calling this {@link #close} method. + */ + protected void finalize() { + if (!getClosed()) + getLogger().debug("Finalizing appender named [{}].", name); + close(); + } + + /** + * A simple linked-list data structure containing filters. + */ + private static class FilterChain { + + private Filter headFilter = null; + private Filter tailFilter = null; + + public synchronized boolean accept(LoggingEvent event) { + Filter f = headFilter; + while (f != null) { + switch (f.decide(event)) { + case Filter.DENY: + return false; + case Filter.ACCEPT: + return true; + case Filter.NEUTRAL: + f = f.getNext(); + } + } + return true; + } + + public synchronized void addFilter(Filter newFilter) { + if (newFilter == null) + throw new NullPointerException(); + if (headFilter == null) { + headFilter = newFilter; + tailFilter = newFilter; + } else { + tailFilter.setNext(newFilter); + tailFilter = newFilter; + } + } + + public synchronized Filter getHead() { + return headFilter; + } + + public synchronized void clear() { + headFilter = null; + tailFilter = null; + } + + public synchronized String toString() { + StringBuffer sb = new StringBuffer(); + Filter f = headFilter; + while (f != null) { + sb.append(f); + f = f.getNext(); + if (f != null) + sb.append(','); + } + return f.toString(); + } + + } + +} Index: src/java/org/apache/log4j/concurrent/PerformanceTest.java =================================================================== --- src/java/org/apache/log4j/concurrent/PerformanceTest.java (revision 0) +++ src/java/org/apache/log4j/concurrent/PerformanceTest.java (revision 0) @@ -0,0 +1,119 @@ +/* + * Copyright 1999,2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.log4j.concurrent; + +import java.io.Writer; +import java.io.StringWriter; +import java.io.FileWriter; + +import org.apache.log4j.Logger; +import org.apache.log4j.Appender; +import org.apache.log4j.AppenderSkeleton; +import org.apache.log4j.PatternLayout; +import org.apache.log4j.spi.OptionHandler; +import org.apache.log4j.spi.LoggingEvent; + +public class PerformanceTest implements Runnable { + + static Logger log = Logger.getLogger(PerformanceTest.class); + + String LAYOUT = "%d{ABSOLUTE} [%24c{1}] (%-8X{con} %X{opr}) %m%n"; + int times = 1000; + int threads = 5; + + public void run() { + for (int i = 0; i < times; i++) { + log.info("Hello world", new Exception()); + } + } + + public PerformanceTest(Appender a) throws Exception { + + log.removeAllAppenders(); + log.addAppender(a); + + Thread t[] = new Thread[threads]; + a.setLayout(new PatternLayout(LAYOUT)); + ((OptionHandler)a).activateOptions(); + + long start = System.currentTimeMillis(); + + for (int i = 0; i < threads; i++) { + t[i] = new Thread(this); + t[i].start(); + } + for (int i = 0; i < threads; i++) { + t[i].join(); + } + + long end = System.currentTimeMillis(); + a.close(); + + System.out.println("Appender " + a.getClass()); + String msg = "Took " + (end - start) + "ms for " + times + " logs * " + " threads " + threads; + System.out.println(msg); + System.out.println(); + } + + public static void main(String s[]) throws Exception { + + System.out.println("Hit CTRL-\\ now"); + Thread.sleep(1000); + + Writer w; + for (int i = 0; i < 5; i++) { + + /* + ConcurrentAppender ca = new ConcurrentAppender() { + protected void append(LoggingEvent event) { + try { Thread.sleep(1); } catch (InterruptedException e) {} + } + protected void internalClose() {} + }; + + AppenderSkeleton as = new AppenderSkeleton() { + protected void append(LoggingEvent event) { + try { Thread.sleep(1); } catch (InterruptedException e) {} + } + public void close() {} + }; + + System.out.println("ConcurrentAppender"); + new PerformanceTest(ca); + + System.out.println("AppenderSkeleton"); + new PerformanceTest(as); + */ + + org.apache.log4j.FileAppender wa = new org.apache.log4j.FileAppender(); + wa.setFile("/tmp/blah"); + new PerformanceTest(wa); + + org.apache.log4j.concurrent.FileAppender wa2 = new org.apache.log4j.concurrent.FileAppender(); + wa2.setFile("/tmp/blah"); + new PerformanceTest(wa2); + /* + */ + + } + + System.out.println("Hit CTRL-\\ now"); + Thread.sleep(1000); + + } + +} Index: src/java/org/apache/log4j/concurrent/ConsoleAppender.java =================================================================== --- src/java/org/apache/log4j/concurrent/ConsoleAppender.java (revision 0) +++ src/java/org/apache/log4j/concurrent/ConsoleAppender.java (revision 0) @@ -0,0 +1,214 @@ +/* + * Copyright 1999,2005 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.log4j.concurrent; + +import java.io.IOException; +import java.io.OutputStream; +import org.apache.log4j.Layout; + + +/** + * ConsoleAppender appends log events to
System.out
or
+ * System.err
using a layout specified by the user. The
+ * default target is System.out
.
+ *
+ * @author Ceki Gülcü
+ * @author Curt Arnold
+ * @since 1.1 */
+public class ConsoleAppender extends WriterAppender {
+ public static final String SYSTEM_OUT = "System.out";
+ public static final String SYSTEM_ERR = "System.err";
+ protected String target = SYSTEM_OUT;
+
+ /**
+ * Determines if the appender honors reassignments of System.out
+ * or System.err made after configuration.
+ */
+ private boolean follow = false;
+
+
+ /**
+ * Constructs an unconfigured appender.
+ */
+ public ConsoleAppender() {
+ }
+
+ /**
+ * Creates a configured appender.
+ *
+ * @param layout layout, may not be null.
+ */
+ public ConsoleAppender(final Layout layout) {
+ setLayout(layout);
+ activateOptions();
+ }
+
+ /**
+ * Creates a configured appender.
+ * @param layout layout, may not be null.
+ * @param targetStr target, either "System.err" or "System.out".
+ */
+ public ConsoleAppender(final Layout layout, final String targetStr) {
+ setLayout(layout);
+ setTarget(targetStr);
+ activateOptions();
+ }
+
+ /**
+ * Sets the value of the Target option. Recognized values
+ * are "System.out" and "System.err". Any other value will be
+ * ignored.
+ * */
+ public void setTarget(final String value) {
+ String v = value.trim();
+
+ if (SYSTEM_OUT.equalsIgnoreCase(v)) {
+ target = SYSTEM_OUT;
+ } else if (SYSTEM_ERR.equalsIgnoreCase(v)) {
+ target = SYSTEM_ERR;
+ } else {
+ getLogger().warn("[{}] should be System.out or System.err.", value);
+ getLogger().warn("Using previously set target, System.out by default.");
+ }
+ }
+
+ /**
+ * Returns the current value of the Target property. The
+ * default value of the option is "System.out".
+ *
+ * See also {@link #setTarget}.
+ * */
+ public String getTarget() {
+ return target;
+ }
+
+ /**
+ * Sets whether the appender honors reassignments of System.out
+ * or System.err made after configuration.
+ * @param newValue if true, appender will use value of System.out or
+ * System.err in force at the time when logging events are appended.
+ * @since 1.2.13
+ */
+ public final void setFollow(final boolean newValue) {
+ follow = newValue;
+ }
+
+ /**
+ * Gets whether the appender honors reassignments of System.out
+ * or System.err made after configuration.
+ * @return true if appender will use value of System.out or
+ * System.err in force at the time when logging events are appended.
+ * @since 1.2.13
+ */
+ public final boolean getFollow() {
+ return follow;
+ }
+
+
+ /**
+ * Prepares the appender for use.
+ */
+ public void activateOptions() {
+ if (follow) {
+ if (target.equals(SYSTEM_ERR)) {
+ setWriter(createWriter(new SystemErrStream()));
+ } else {
+ setWriter(createWriter(new SystemOutStream()));
+ }
+ } else {
+ if (target.equals(SYSTEM_ERR)) {
+ setWriter(createWriter(System.err));
+ } else {
+ setWriter(createWriter(System.out));
+ }
+ }
+
+ super.activateOptions();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ protected
+ final
+ void closeWriter() {
+ if (follow) {
+ super.closeWriter();
+ }
+ }
+
+
+ /**
+ * An implementation of OutputStream that redirects to the
+ * current System.err.
+ *
+ */
+ private static class SystemErrStream extends OutputStream {
+ public SystemErrStream() {
+ }
+
+ public void close() {
+ }
+
+ public void flush() {
+ System.err.flush();
+ }
+
+ public void write(final byte[] b) throws IOException {
+ System.err.write(b);
+ }
+
+ public void write(final byte[] b, final int off, final int len)
+ throws IOException {
+ System.err.write(b, off, len);
+ }
+
+ public void write(final int b) throws IOException {
+ System.err.write(b);
+ }
+ }
+
+ /**
+ * An implementation of OutputStream that redirects to the
+ * current System.out.
+ *
+ */
+ private static class SystemOutStream extends OutputStream {
+ public SystemOutStream() {
+ }
+
+ public void close() {
+ }
+
+ public void flush() {
+ System.out.flush();
+ }
+
+ public void write(final byte[] b) throws IOException {
+ System.out.write(b);
+ }
+
+ public void write(final byte[] b, final int off, final int len)
+ throws IOException {
+ System.out.write(b, off, len);
+ }
+
+ public void write(final int b) throws IOException {
+ System.out.write(b);
+ }
+ }
+}
+
Index: src/java/org/apache/log4j/concurrent/WriterAppender.java
===================================================================
--- src/java/org/apache/log4j/concurrent/WriterAppender.java (revision 0)
+++ src/java/org/apache/log4j/concurrent/WriterAppender.java (revision 0)
@@ -0,0 +1,314 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.concurrent;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import org.apache.log4j.Layout;
+
+// Contibutors: Jens Uwe Pipka immediateFlush
is set to
+ false
, then there is a good chance that the last few
+ logs events are not actually written to persistent media if and
+ when the application crashes.
+
+ The immediateFlush
variable is set to
+ true
by default.
+
+ */
+ protected boolean immediateFlush = true;
+
+ /**
+ The encoding to use when opening an InputStream.
The
+ 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;
+ }
+
+ /**
+ Returns value of the ImmediateFlush option.
+ */
+ public boolean getImmediateFlush() {
+ return immediateFlush;
+ }
+
+ /**
+ * Activates options. Should be called only once.
+ */
+ public void activateOptions() {
+
+ if (getLayout() == null) {
+ getLogger().error(
+ "No layout set for the appender named [{}].", name);
+ return;
+ }
+
+ if (this.writer == null) {
+ getLogger().error(
+ "No writer set for the appender named [{}].", name);
+ return;
+ }
+
+ active.set(true);
+
+ }
+
+ /**
+ This method is called by the {@link AppenderSkeleton#doAppend}
+ method.
+
+ If the output stream exists and is writable then write a log
+ statement to the output stream. Otherwise, write a single warning
+ message to The format of the output will depend on this appender's
+ layout.
+
+ */
+ public void append(LoggingEvent event) {
+ subAppend(event);
+ }
+
+ /**
+ Close this appender instance. The underlying stream or writer is
+ also closed.
+ Closed appenders cannot be reused.
+ */
+ protected void internalClose() {
+ closeWriter();
+ }
+
+ /**
+ * Close the underlying {@link java.io.Writer}.
+ */
+ protected void closeWriter() {
+ getWriteLock();
+ try {
+ if (this.writer == null)
+ return;
+ try {
+ // before closing we have to output the footer
+ writeFooter();
+ this.writer.close();
+ this.writer = null;
+ } catch (IOException e) {
+ getLogger().error("Could not close writer for WriterAppener named "+name, e);
+ }
+ } finally {
+ releaseWriteLock();
+ }
+ }
+
+ /**
+ Returns an OutputStreamWriter when passed an OutputStream. The
+ encoding used will depend on the value of the
+ Sets the Writer where the log output will go. The
+ specified Writer must be opened by the user and be
+ writable.
+
+ The WARNING: Logging to an unopened Writer will fail.
+
+ @param writer An already opened Writer. */
+ public void setWriter(Writer writer) {
+ // close any previously opened writer
+ closeWriter();
+
+ getWriteLock();
+ try {
+ this.writer = writer;
+ writeHeader();
+ } finally {
+ releaseWriteLock();
+ }
+ }
+
+ /**
+ * Actual writing occurs here.
+ * Most subclasses of Support for This option is meaningful only if the FileAppender opens the file.
+ */
+ protected boolean fileAppend = true;
+
+ /**
+ The name of the log file. */
+ protected String fileName = null;
+
+ /**
+ Do we do bufferedIO? */
+ protected boolean bufferedIO = true;
+
+ /**
+ The size of the IO buffer. Default is 8K. */
+ protected int bufferSize = DEFAULT_BUFFER_SIZE;
+
+ /**
+ The default constructor does not do anything.
+ */
+ public FileAppender() {
+ }
+
+ /**
+ Instantiate a If the If the If the The file will be appended to. */
+ public FileAppender(Layout layout, String filename) throws IOException {
+ this(layout, filename, true);
+ activateOptions();
+ }
+
+ /**
+ The File property takes a string value which should be the
+ name of the file to append to.
+
+ Note that the special values
+ "System.out" or "System.err" are no longer honored.
+
+ Note: Actual opening of the file is made when {@link
+ #activateOptions} is called, not when the options are set. */
+ public void setFile(String file) {
+ // Trim spaces from both ends. The users probably does not want
+ // trailing spaces in file names.
+ String val = file.trim();
+ fileName = OptionConverter.stripDuplicateBackslashes(val);
+ }
+
+ /**
+ Returns the value of the Append option.
+ */
+ public boolean getAppend() {
+ return fileAppend;
+ }
+
+ /** Returns the value of the File option. */
+ public String getFile() {
+ return fileName;
+ }
+
+ /**
+ If the value of File is not BufferedIO will significantly increase performance on heavily
+ loaded systems.
+
+ */
+ public boolean getBufferedIO() {
+ return this.bufferedIO;
+ }
+
+ /**
+ Get the size of the IO buffer.
+ */
+ public int getBufferSize() {
+ return this.bufferSize;
+ }
+
+ /**
+ The Append option takes a boolean value. It is set to
+ Note: Actual opening of the file is made when {@link
+ #activateOptions} is called, not when the options are set.
+ */
+ public void setAppend(boolean flag) {
+ fileAppend = flag;
+ }
+
+ /**
+ The BufferedIO option takes a boolean value. It is set to
+ Sets and opens the file where the log output will
+ go. The specified file must be writable.
+
+ If there was already an opened file, then the previous file
+ is closed first.
+
+ Do not use this method directly. To configure a FileAppender
+ or one of its subclasses, set its properties one by one and then
+ call activateOptions.
+
+ @param filename The path to the log file.
+ @param append If true will append to fileName. Otherwise will
+ truncate fileName.
+ @param bufferedIO
+ @param bufferSize
+
+ @throws IOException
+
+ */
+ public void setFile(
+ String filename, boolean append, boolean bufferedIO, int bufferSize)
+ throws IOException {
+ getLogger().debug("setFile called: {}, {}", fileName, append?"true":"false");
+
+ FileOutputStream ostream;
+ try {
+ //
+ // attempt to create file
+ //
+ ostream = new FileOutputStream(filename, append);
+ } catch(FileNotFoundException ex) {
+ //
+ // if parent directory does not exist then
+ // attempt to create it and try to create file
+ // see bug 9150
+ //
+ File parentDir = new File(new File(filename).getParent());
+ if(!parentDir.exists() && parentDir.mkdirs()) {
+ ostream = new FileOutputStream(filename, append);
+ } else {
+ throw ex;
+ }
+ }
+ Writer writer = createWriter(ostream);
+
+ if (bufferedIO) {
+ writer = new BufferedWriter(writer, bufferSize);
+ }
+
+ this.fileAppend = append;
+ this.bufferedIO = bufferedIO;
+ this.fileName = filename;
+ this.bufferSize = bufferSize;
+
+ setWriter(writer);
+ getLogger().debug("setFile ended");
+ }
+
+}
Index: src/java/org/apache/log4j/HTMLLayout.java
===================================================================
--- src/java/org/apache/log4j/HTMLLayout.java (revision 375405)
+++ src/java/org/apache/log4j/HTMLLayout.java (working copy)
@@ -38,6 +38,10 @@
* are specified using a conversion pattern. See
* {@link org.apache.log4j.PatternLayout} for documentation on the available
* patterns.
+
+ This class is thread safe; it may be called by multiple threads
+ simultaneously to format messages.
+
*
* @author Ceki Gülcü
* @author Steve Mactaggart
@@ -103,7 +107,7 @@
// counter keeping track of the rows output
- private long counter = 0;
+ private int counter = 0;
/**
* Constructs a PatternLayout using the DEFAULT_LAYOUT_PATTERN.
@@ -369,9 +373,9 @@
*/
public String format(LoggingEvent event) {
- boolean odd = true;
- if(((counter++) & 1) == 0) {
- odd = false;
+ boolean odd;
+ synchronized (this) {
+ odd = ((counter++) & 1) != 0;
}
String level = event.getLevel().toString().toLowerCase();
Index: src/java/org/apache/log4j/AppenderSkeleton.java
===================================================================
--- src/java/org/apache/log4j/AppenderSkeleton.java (revision 375405)
+++ src/java/org/apache/log4j/AppenderSkeleton.java (working copy)
@@ -54,7 +54,7 @@
*
* @deprecated as of 1.3
*/
- protected org.apache.log4j.spi.ErrorHandler errorHandler = new org.apache.log4j.helpers.OnlyOnceErrorHandler();
+ protected org.apache.log4j.spi.ErrorHandler errorHandler = org.apache.log4j.helpers.OnlyOnceErrorHandler.INSTANCE;
/**
* The first filter in the filter chain. Set to This class is thread safe; it may be called by multiple threads
+ simultaneously to format messages.
+
@author James P. Cakalic
@author Ceki Gülcü
@@ -455,11 +458,6 @@
private FormattingInfo[] patternFields;
/**
- * String buffer used in formatting.
- */
- private StringBuffer buf = new StringBuffer();
-
- /**
* True if any element in pattern formats information from exceptions.
*/
private boolean handlesExceptions;
@@ -550,21 +548,16 @@
}
/**
- * Formats a logging event to a writer.
- * @param event logging event to be formatted.
- */
- public String format(final LoggingEvent event) {
- buf.setLength(0);
-
+ * Formats an event.
+ */
+ public String format(LoggingEvent event) {
+ StringBuffer buf = new StringBuffer(80);
for (int i = 0; i < patternConverters.length; i++) {
int startField = buf.length();
patternConverters[i].format(event, buf);
patternFields[i].format(startField, buf);
}
-
- String retval = buf.toString();
- buf.setLength(0);
- return retval;
+ return buf.toString();
}
/**
Index: src/java/org/apache/log4j/spi/LocationInfo.java
===================================================================
--- src/java/org/apache/log4j/spi/LocationInfo.java (revision 375405)
+++ src/java/org/apache/log4j/spi/LocationInfo.java (working copy)
@@ -106,9 +106,9 @@
}
if(PlatformInfo.hasStackTraceElement()) {
- StackTraceElementExtractor.extract(this, t, fqnOfInvokingClass);
+ // StackTraceElementExtractor.extract(this, t, fqnOfInvokingClass);
} else {
- LegacyExtractor.extract(this, t, fqnOfInvokingClass);
+ // LegacyExtractor.extract(this, t, fqnOfInvokingClass);
}
}
Index: src/java/org/apache/log4j/spi/location/LegacyExtractor.java
===================================================================
--- src/java/org/apache/log4j/spi/location/LegacyExtractor.java (revision 375405)
+++ src/java/org/apache/log4j/spi/location/LegacyExtractor.java (working copy)
@@ -22,9 +22,7 @@
import java.io.PrintWriter;
import java.io.StringWriter;
-import org.apache.log4j.spi.LocationInfo;
-
/**
* Extract location information from a throwable. The techniques used here
* work on all JDK platforms including those prior to JDK 1.4.
Index: src/java/org/apache/log4j/spi/location/StackTraceElementExtractor.java
===================================================================
--- src/java/org/apache/log4j/spi/location/StackTraceElementExtractor.java (revision 375405)
+++ src/java/org/apache/log4j/spi/location/StackTraceElementExtractor.java (working copy)
@@ -15,7 +15,6 @@
*/
package org.apache.log4j.spi.location;
-import org.apache.log4j.spi.LocationInfo;
import java.lang.reflect.Method;
Index: src/java/org/apache/log4j/helpers/SyslogWriter.java
===================================================================
--- src/java/org/apache/log4j/helpers/SyslogWriter.java (revision 375405)
+++ src/java/org/apache/log4j/helpers/SyslogWriter.java (working copy)
@@ -36,7 +36,7 @@
@since 0.7.3
*/
public class SyslogWriter extends Writer {
- final int SYSLOG_PORT = 514;
+ final static int SYSLOG_PORT = 514;
String syslogHost;
private InetAddress address;
private DatagramSocket ds;
Index: src/java/org/apache/log4j/helpers/LogLog.java
===================================================================
--- src/java/org/apache/log4j/helpers/LogLog.java (revision 375405)
+++ src/java/org/apache/log4j/helpers/LogLog.java (working copy)
@@ -64,7 +64,7 @@
Note that the search for all option names is case sensitive. */
public static final String CORE_DEBUG_KEY = "log4j.coreDebug";
- protected static boolean debugEnabled = false;
+ private static boolean debugEnabled = false;
private static final String PREFIX = "log4j: ";
private static final String ERR_PREFIX = "log4j:ERROR ";
@@ -87,6 +87,13 @@
}
/**
+ Returns true if debug is enabled.
+ */
+ public static boolean isDebugEnabled() {
+ return debugEnabled;
+ }
+
+ /**
This method is used to output log4j internal debug
statements. Output goes to A PatternConverter should be written to be thread-safe.
+ This allows multiple threads to safely use the same pattern converter.
+
@author James P. Cakalic
@author Ceki Gülcü
@author Chris Nokes
@@ -56,6 +59,8 @@
/**
* Formats an object into a string buffer.
+ * This method may be called simultaneously by multiple threads.
+ *
* @param obj event to format, may not be null.
* @param toAppendTo string buffer to which the formatted event will be appended. May not be null.
*/
Index: src/java/org/apache/log4j/FileAppender.java
===================================================================
--- src/java/org/apache/log4j/FileAppender.java (revision 375405)
+++ src/java/org/apache/log4j/FileAppender.java (working copy)
@@ -39,6 +39,9 @@
* @author Ceki Gülcü
* */
public class FileAppender extends WriterAppender {
+
+ public static final int DEFAULT_BUFFER_SIZE = 8 * 1024;
+
/**
* Controls whether to append to or truncate an existing file.
* The default value for this variable is
@@ -55,11 +58,11 @@
/**
Do we do bufferedIO? */
- protected boolean bufferedIO = false;
+ protected boolean bufferedIO = true;
/**
The size of the IO buffer. Default is 8K. */
- protected int bufferSize = 8 * 1024;
+ protected int bufferSize = DEFAULT_BUFFER_SIZE;
/**
The default constructor does not do anything.
@@ -176,7 +179,7 @@
/**
Get the value of the BufferedIO option.
- BufferedIO will significatnly increase performance on heavily
+ BufferedIO will significantly increase performance on heavily
loaded systems.
*/
@@ -211,16 +214,12 @@
will be opened and the resulting {@link java.io.Writer} wrapped
around a {@link BufferedWriter}.
- BufferedIO will significatnly increase performance on heavily
+ BufferedIO will significantly increase performance on heavily
loaded systems.
*/
public void setBufferedIO(boolean bufferedIO) {
this.bufferedIO = bufferedIO;
-
- if (bufferedIO) {
- immediateFlush = false;
- }
}
/**
@@ -255,14 +254,9 @@
throws IOException {
getLogger().debug("setFile called: {}, {}", fileName, append?"true":"false");
- // It does not make sense to have immediate flush and bufferedIO.
- if (bufferedIO) {
- setImmediateFlush(false);
- }
-
closeWriter();
- FileOutputStream ostream = null;
+ FileOutputStream ostream;
try {
//
// attempt to create file
Index: src/java/org/apache/log4j/plugins/Receiver.java
===================================================================
--- src/java/org/apache/log4j/plugins/Receiver.java (revision 375405)
+++ src/java/org/apache/log4j/plugins/Receiver.java (working copy)
@@ -59,14 +59,6 @@
public abstract class Receiver extends PluginSkeleton implements Thresholdable {
protected Level thresholdLevel;
- /*
- * An instance specific logger which must be accessed through the getLogger()
- * method.
- */
- private Logger logger;
-
-
-
/**
Sets the receiver theshold to the given level.
Index: src/java/org/apache/log4j/net/SyslogAppender.java
===================================================================
--- src/java/org/apache/log4j/net/SyslogAppender.java (revision 375405)
+++ src/java/org/apache/log4j/net/SyslogAppender.java (working copy)
@@ -128,7 +128,6 @@
//SyslogTracerPrintWriter stp;
private SyslogWriter sw;
- private final Calendar calendar = Calendar.getInstance();
private long now = -1;
private Date date = new Date();
private StringBuffer timestamp = new StringBuffer();
Index: src/java/org/apache/log4j/net/SocketNode.java
===================================================================
--- src/java/org/apache/log4j/net/SocketNode.java (revision 375405)
+++ src/java/org/apache/log4j/net/SocketNode.java (working copy)
@@ -53,7 +53,6 @@
private boolean paused;
private Socket socket;
private Receiver receiver;
- private SocketNodeEventListener listener;
private List listenerList = Collections.synchronizedList(new ArrayList());
/**
@@ -186,7 +185,7 @@
}
// send event to listener, if configured
- if (listener != null || listenerList.size()>0) {
+ if (listenerList.size()>0) {
fireSocketClosedEvent(listenerException);
}
}
Index: src/java/org/apache/log4j/xml/XMLLayout.java
===================================================================
--- src/java/org/apache/log4j/xml/XMLLayout.java (revision 375405)
+++ src/java/org/apache/log4j/xml/XMLLayout.java (working copy)
@@ -59,6 +59,9 @@
* for output generated by log4j versions prior to log4j 1.2 (final release) and
* "1.2" for relase 1.2 and later.
*
+ This class is thread safe; it may be called by multiple threads
+ simultaneously to format messages.
+
* Contributors: Mathias Bogaert
*
* @author Ceki Gülcü
@@ -207,7 +210,7 @@
String propName = propIter.next().toString();
buf.append(" This class is thread safe; it may be called by multiple threads
+ simultaneously to format messages.
+
@author Ceki Gülcü
@since version 0.7.0
@@ -36,7 +39,6 @@
{@link PatternLayout} offers a much more powerful alternative.
*/
public class SimpleLayout extends Layout {
- StringBuffer sbuf = new StringBuffer(128);
public SimpleLayout() {
}
Index: src/java/org/apache/log4j/scheduler/Scheduler.java
===================================================================
--- src/java/org/apache/log4j/scheduler/Scheduler.java (revision 375405)
+++ src/java/org/apache/log4j/scheduler/Scheduler.java (working copy)
@@ -82,7 +82,7 @@
// if the job is the first on the list, then notify the scheduler thread
// to schedule a new job
if(i == 0) {
- this.notify();
+ this.notifyAll();
}
return true;
} else {
@@ -154,11 +154,11 @@
jobList.add(i, newSJE);
// if the jobList was empty, then notify the scheduler thread
if(i == 0) {
- this.notify();
+ this.notifyAll();
}
}
- public void shutdown() {
+ public synchronized void shutdown() {
shutdown = true;
}
Index: tests/src/java/org/apache/log4j/DeadlockTest.java
===================================================================
--- tests/src/java/org/apache/log4j/DeadlockTest.java (revision 375405)
+++ tests/src/java/org/apache/log4j/DeadlockTest.java (working copy)
@@ -22,8 +22,6 @@
/**
* Test case for bug http://nagoya.apache.org/bugzilla/show_bug.cgi?id=24159
*
- * Actually this one is impossible to fix.
- *
* @author Elias Ross
* @author Ceki Gulcu
*/
@@ -31,14 +29,18 @@
static long RUNLENGTH = 10000;
Logger logger = Logger.getLogger("DeadlockTest");
- public DeadlockTest() {
- super("DeadlockTest");
+ public DeadlockTest(String name) {
+ super(name);
}
protected void setUp() throws Exception {
super.setUp();
System.out.println("in setup");
- BasicConfigurator.configure();
+
+ Logger root = Logger.getRootLogger();
+ root.addAppender(
+ new org.apache.log4j.concurrent.ConsoleAppender(
+ new PatternLayout(PatternLayout.TTCC_CONVERSION_PATTERN)));
}
protected void tearDown() throws Exception {
Index: tests/build.xml
===================================================================
--- tests/build.xml (revision 375405)
+++ tests/build.xml (working copy)
@@ -798,6 +798,14 @@
+ encoding
variable is set to null by
+ default which results in the utilization of the system's default
+ encoding. */
+ protected String encoding;
+
+ /**
+ * This is the {@link Writer Writer} where we will write to.
+ * Do not directly use this object without obtaining a write lock.
+ */
+ protected Writer writer;
+
+ /**
+ * The default constructor does nothing.
+ * */
+ public WriterAppender() {
+ super(false);
+ }
+
+ /**
+ If the ImmediateFlush option is set to
+
true
, the appender will flush at the end of each
+ write. This is the default behavior. If the option is set to
+ false
, then the underlying stream can defer writing
+ to physical medium to a later time.
+
+ System.err
.
+
+ encoding
property. If the encoding value is
+ specified incorrectly the writer will be opened using the default
+ system encoding (an error message will be printed to the loglog. */
+ protected OutputStreamWriter createWriter(OutputStream os) {
+ OutputStreamWriter retval = null;
+
+ String enc = getEncoding();
+
+ if (enc != null) {
+ try {
+ retval = new OutputStreamWriter(os, enc);
+ } catch (IOException e) {
+ getLogger().warn("Error initializing output writer.");
+ getLogger().warn("Unsupported encoding?");
+ }
+ }
+
+ if (retval == null) {
+ retval = new OutputStreamWriter(os);
+ }
+
+ return retval;
+ }
+
+ public String getEncoding() {
+ return encoding;
+ }
+
+ public void setEncoding(String value) {
+ encoding = value;
+ }
+
+ /**
+ java.io.Writer
will be closed when the
+ appender instance is closed.
+
+
+ WriterAppender
will need to override
+ * this method.
+ */
+ protected void subAppend(LoggingEvent event) {
+ try {
+
+ // Format first
+ Layout layout = getLayout();
+ String se = layout.format(event);
+ String st[] = null;
+ if (layout.ignoresThrowable()) {
+ st = event.getThrowableStrRep();
+ }
+
+ // Write as one message
+ synchronized (this.writer) {
+ this.writer.write(se);
+ if (st != null) {
+ int len = st.length;
+ for (int i = 0; i < len; i++) {
+ this.writer.write(st[i]);
+ this.writer.write(Layout.LINE_SEP);
+ }
+ }
+ }
+
+ if (this.immediateFlush)
+ this.writer.flush();
+
+ } catch (IOException ioe) {
+ boolean wasOrder = active.set(false);
+ if (wasOrder) {
+ getLogger().error("IO failure for appender named "+name, ioe);
+ }
+ }
+ }
+
+ /**
+ The WriterAppender requires a layout. Hence, this method returns
+ true
.
+ */
+ public boolean requiresLayout() {
+ return true;
+ }
+
+ /**
+ * Write a footer as produced by the embedded layout's {@link
+ * Layout#getFooter} method.
+ */
+ protected void writeFooter() {
+ Layout layout = getLayout();
+ if (layout != null) {
+ String f = layout.getFooter();
+
+ if ((f != null) && (this.writer != null)) {
+ try {
+ this.writer.write(f);
+ } catch(IOException ioe) {
+ active.set(false);
+ getLogger().error("Failed to write footer for Appender named "+name, ioe);
+ }
+ }
+ }
+ }
+
+ /**
+ * Write a header as produced by the embedded layout's {@link
+ * Layout#getHeader} method.
+ */
+ protected void writeHeader() {
+ Layout layout = getLayout();
+ if (layout != null) {
+ String h = layout.getHeader();
+
+ if ((h != null) && (this.writer != null)) {
+ try {
+ this.writer.write(h);
+ this.writer.flush();
+ } catch(IOException ioe) {
+ active.set(false);
+ getLogger().error("Failed to write header for WriterAppender named "+name, ioe);
+ }
+ }
+ }
+ }
+
+
+}
Index: src/java/org/apache/log4j/concurrent/FileAppender.java
===================================================================
--- src/java/org/apache/log4j/concurrent/FileAppender.java (revision 0)
+++ src/java/org/apache/log4j/concurrent/FileAppender.java (revision 0)
@@ -0,0 +1,290 @@
+/*
+ * Copyright 1999,2004 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.log4j.concurrent;
+
+import java.io.Writer;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.helpers.OptionConverter;
+
+
+// Contibutors: Jens Uwe Pipka java.io.Writer
and console appending
+ * has been deprecated and then removed. See the replacement
+ * solutions: {@link WriterAppender} and {@link ConsoleAppender}.
+ *
+ * @author Ceki Gülcü
+ * */
+public class FileAppender extends WriterAppender {
+
+ public static final int DEFAULT_BUFFER_SIZE = 8 * 1024;
+
+ /**
+ * Controls whether to append to or truncate an existing file.
+ * The default value for this variable is
+ * true
, meaning that by default a FileAppender
will
+ * append to an existing file and not truncate it.
+ *
+ * FileAppender
and open the file
+ designated by filename
. The opened filename will
+ become the output destination for this appender.
+
+ append
parameter is true, the file will be
+ appended to. Otherwise, the file designated by
+ filename
will be truncated before being opened.
+
+ bufferedIO
parameter is true
,
+ then buffered IO will be used to write to the output file.
+
+ */
+ public FileAppender(
+ Layout layout, String filename, boolean append, boolean bufferedIO,
+ int bufferSize) throws IOException {
+ setLayout(layout);
+ this.setFile(filename, append, bufferedIO, bufferSize);
+ activateOptions();
+ }
+
+ /**
+ Instantiate a FileAppender and open the file designated by
+ filename
. The opened filename will become the output
+ destination for this appender.
+
+ append
parameter is true, the file will be
+ appended to. Otherwise, the file designated by
+ filename
will be truncated before being opened.
+ */
+ public FileAppender(Layout layout, String filename, boolean append)
+ throws IOException {
+ this(layout, filename, append, false, DEFAULT_BUFFER_SIZE);
+ }
+
+ /**
+ Instantiate a FileAppender and open the file designated by
+ filename
. The opened filename will become the output
+ destination for this appender.
+
+ null
, then {@link
+ #setFile} is called with the values of File and
+ Append properties.
+
+ @since 0.8.1 */
+ public void activateOptions() {
+ if (fileName != null) {
+ try {
+ setFile(fileName, fileAppend, bufferedIO, bufferSize);
+ super.activateOptions();
+ } catch (java.io.IOException e) {
+ getLogger().error(
+ "setFile(" + fileName + "," + fileAppend + ") call failed.", e);
+ }
+ } else {
+ getLogger().error("File option not set for appender [{}].", name);
+ getLogger().warn("Are you using FileAppender instead of ConsoleAppender?");
+ }
+ }
+
+ /**
+ * Closes the previously opened file.
+ *
+ * @deprecated Use the super class' {@link #closeWriter} method instead.
+ */
+ protected void closeFile() {
+ closeWriter();
+ }
+
+ /**
+ Get the value of the BufferedIO option.
+
+ true
by default. If true, then File
+ will be opened in append mode by {@link #setFile setFile} (see
+ above). Otherwise, {@link #setFile setFile} will open
+ File
in truncate mode.
+
+ false
by default. If true, then File
+ will be opened and the resulting {@link java.io.Writer} wrapped
+ around a {@link BufferedWriter}.
+
+ BufferedIO will significantly increase performance on heavily
+ loaded systems.
+
+ */
+ public void setBufferedIO(boolean bufferedIO) {
+ this.bufferedIO = bufferedIO;
+ }
+
+ /**
+ Set the size of the IO buffer.
+ */
+ public void setBufferSize(int bufferSize) {
+ this.bufferSize = bufferSize;
+ }
+
+ /**
+ null
initially.
@@ -165,13 +165,13 @@
/**
* Return the hardcoded OnlyOnceErrorHandler
for this Appender.
- * ErrorHandler
's are no longer utilized as of version 1.3.
+ * ErrorHandler
s are no longer utilized as of version 1.3.
*
* @since 0.9.0
* @deprecated As of 1.3
*/
public org.apache.log4j.spi.ErrorHandler getErrorHandler() {
- return this.errorHandler;
+ return org.apache.log4j.helpers.OnlyOnceErrorHandler.INSTANCE;
}
/**
@@ -199,7 +199,7 @@
}
/**
- * Returns the name of this FileAppender.
+ * Returns the name of this appender.
*/
public final String getName() {
return this.name;
Index: src/java/org/apache/log4j/PatternLayout.java
===================================================================
--- src/java/org/apache/log4j/PatternLayout.java (revision 375405)
+++ src/java/org/apache/log4j/PatternLayout.java (working copy)
@@ -405,6 +405,9 @@
Philip E. Margolis' highly recommended book "C -- a Software
Engineering Approach", ISBN 0-387-97389-3.
+ System.out
.
*/
Index: src/java/org/apache/log4j/helpers/OnlyOnceErrorHandler.java
===================================================================
--- src/java/org/apache/log4j/helpers/OnlyOnceErrorHandler.java (revision 375405)
+++ src/java/org/apache/log4j/helpers/OnlyOnceErrorHandler.java (working copy)
@@ -19,6 +19,7 @@
import org.apache.log4j.Appender;
import org.apache.log4j.Logger;
import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.ErrorHandler;
/**
ErrorHandler
and its implementations are no longer
@@ -32,6 +33,12 @@
@deprecated As of 1.3
*/
public class OnlyOnceErrorHandler implements org.apache.log4j.spi.ErrorHandler {
+
+ /**
+ * Default instance of this class.
+ */
+ public static final ErrorHandler INSTANCE = new OnlyOnceErrorHandler();
+
public void setLogger(Logger logger) {}
public void activateOptions() {}
public void error(String message, Exception e, int errorCode) {}
Index: src/java/org/apache/log4j/joran/JoranConfigurator.java
===================================================================
--- src/java/org/apache/log4j/joran/JoranConfigurator.java (revision 375405)
+++ src/java/org/apache/log4j/joran/JoranConfigurator.java (working copy)
@@ -71,7 +71,6 @@
public class JoranConfigurator extends ConfiguratorBase {
Interpreter joranInterpreter;
LoggerRepository repository;
- boolean listAppnderAttached = false;
public JoranConfigurator() {
}
Index: src/java/org/apache/log4j/WriterAppender.java
===================================================================
--- src/java/org/apache/log4j/WriterAppender.java (revision 375405)
+++ src/java/org/apache/log4j/WriterAppender.java (working copy)
@@ -24,7 +24,6 @@
import java.io.OutputStreamWriter;
import java.io.Writer;
import org.apache.log4j.helpers.QuietWriter;
-import org.apache.log4j.helpers.LogLog;
// Contibutors: Jens Uwe Pipka