Skip to content

Commit

Permalink
propagate immediateFlush from encoder to enclosing OSAppender for bac…
Browse files Browse the repository at this point in the history
…kward compatibility
  • Loading branch information
ceki committed Feb 9, 2017
1 parent e45ce8b commit d3d704c
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 72 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<configuration>

<appender name="LIOE" class="ch.qos.logback.core.FileAppender">
<file>target/test-output/layoutInsteadOfEncoder.log</file>
<append>true</append>

<encoder>
<immediateFlush>${immediateFlush}</immediateFlush>
<pattern>%msg%n</pattern>
</encoder>
</appender>

<root level="debug">
<appender-ref ref="LIOE" />
</root>
</configuration>
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,35 @@ public void layoutInsteadOfEncoer() throws JoranException {
assertTrue(fileAppender.isStarted());
assertTrue(fileAppender.getEncoder() instanceof LayoutWrappingEncoder);
}

@Test
public void immediateFlushInEncoder_TRUE() throws JoranException {
immediateFlushInEncoder(true);
}

@Test
public void immediateFlushInEncoder_FALSE() throws JoranException {
immediateFlushInEncoder(false);
}

public void immediateFlushInEncoder(Boolean immediateFlush) throws JoranException {
loggerContext.putProperty("immediateFlush", immediateFlush.toString());
jc.doConfigure(ClassicTestConstants.JORAN_INPUT_PREFIX + "compatibility/immediateFlushInEncoder.xml");
StatusPrinter.print(loggerContext);
StatusChecker checker = new StatusChecker(loggerContext);

checker.assertContainsMatch(Status.WARN, "As of version 1.2.0 \"immediateFlush\" property should be set within the enclosing Appender.");
checker.assertContainsMatch(Status.WARN, "Please move \"immediateFlush\" property into the enclosing appender.");
checker.assertContainsMatch(Status.WARN, "Setting the \"immediateFlush\" property of the enclosing appender to "+immediateFlush);

ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) loggerContext.getLogger(Logger.ROOT_LOGGER_NAME);
FileAppender<ILoggingEvent> fileAppender = (FileAppender<ILoggingEvent>) root.getAppender("LIOE");
assertTrue(fileAppender.isStarted());
assertEquals(immediateFlush.booleanValue(), fileAppender.isImmediateFlush());
}

private void assertEquals(boolean immediateFlush) {
// TODO Auto-generated method stub

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import ch.qos.logback.core.CoreConstants;
import ch.qos.logback.core.Layout;
import ch.qos.logback.core.OutputStreamAppender;

public class LayoutWrappingEncoder<E> extends EncoderBase<E> {

Expand All @@ -31,22 +32,8 @@ public class LayoutWrappingEncoder<E> extends EncoderBase<E> {
*/
private Charset charset;

private boolean immediateFlush = true;

/**
* Sets the immediateFlush option. The default value for immediateFlush is 'true'. If set to true,
* the doEncode() method will immediately flush the underlying OutputStream. Although immediate flushing
* is safer, it also significantly degrades logging throughput.
*
* @since 1.0.3
*/
public void setImmediateFlush(boolean immediateFlush) {
this.immediateFlush = immediateFlush;
}

public boolean isImmediateFlush() {
return immediateFlush;
}
OutputStreamAppender<?> parent;
Boolean immediateFlush = null;

public Layout<E> getLayout() {
return layout;
Expand Down Expand Up @@ -74,6 +61,19 @@ public void setCharset(Charset charset) {
this.charset = charset;
}

/**
* Sets the immediateFlush option. The default value for immediateFlush is 'true'. If set to true,
* the doEncode() method will immediately flush the underlying OutputStream. Although immediate flushing
* is safer, it also significantly degrades logging throughput.
*
* @since 1.0.3
*/
public void setImmediateFlush(boolean immediateFlush) {
addWarn("As of version 1.2.0 \"immediateFlush\" property should be set within the enclosing Appender.");
addWarn("Please move \"immediateFlush\" property into the enclosing appender.");
this.immediateFlush = immediateFlush;
}

@Override
public byte[] headerBytes() {
if (layout == null)
Expand All @@ -91,7 +91,6 @@ public byte[] headerBytes() {
return convertToBytes(sb.toString());
}


@Override
public byte[] footerBytes() {
if (layout == null)
Expand Down Expand Up @@ -121,6 +120,16 @@ public boolean isStarted() {
}

public void start() {
if (immediateFlush != null) {
if (parent instanceof OutputStreamAppender) {
addWarn("Setting the \"immediateFlush\" property of the enclosing appender to " + immediateFlush);
@SuppressWarnings("unchecked")
OutputStreamAppender<E> parentOutputStreamAppender = (OutputStreamAppender<E>) parent;
parentOutputStreamAppender.setImmediateFlush(immediateFlush);
} else {
addError("Could not set the \"immediateFlush\" property of the enclosing appender.");
}
}
started = true;
}

Expand All @@ -134,4 +143,13 @@ private void appendIfNotNull(StringBuilder sb, String s) {
}
}

/**
* This method allows RollingPolicy implementations to be aware of their
* containing appender.
*
* @param appender
*/
public void setParent(OutputStreamAppender<?> parent) {
this.parent = parent;
}
}
5 changes: 3 additions & 2 deletions logback-examples/src/main/resources/chapters/appenders/conf/logback-fileAppender.xml
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>testFile.log</file>
<append>true</append>

<!-- encoders are assigned the type
<!-- set immediateFlush to false for much higher logging throughput -->
<immediateFlush>true</immediateFlush>
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
Expand Down
29 changes: 22 additions & 7 deletions logback-site/src/site/pages/manual/appenders.html
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,24 @@ <h1>Logback-core</h1>
underlying <code>OutputStreamAppender</code>. Encoders are
described in a <a href="encoders.html">dedicated chapter</a>.
</td>
</tr>
</tr>
<tr>
<td><span class="prop" name="immediateFlush">immediateFlush</span></td>
<td><code>boolean</code></td>

<td>The default value for <span
class="option">immediateFlush</span> is 'true'. Immediate
flushing of the output stream ensures that logging events are
immediately written out and will not be lost in case your
application exits without properly closing appenders. On the
other hand, setting this property to 'false' is likely to
quadruple (your mileage may vary) logging throughput. Again, if
<span class="option">immediateFlush</span> is set to 'false' and
if appenders are not closed properly when your application exits,
then logging events not yet written to disk may be lost.
</td>
</tr>


</table>

Expand Down Expand Up @@ -435,7 +452,6 @@ <h2 class="doAnchor" name="FileAppender">FileAppender</h2>
</p>
</td>
</tr>


<tr>
<td><span class="prop" name="prudent">prudent</span></td>
Expand Down Expand Up @@ -509,11 +525,8 @@ <h2 class="doAnchor" name="FileAppender">FileAppender</h2>
events are not lost in case your application exits without properly
closing appenders. However, for significantly increased logging
throughput, you may want to set the <span
class="prop">immediateFlush</span> property of the underlying
<code>Encoder</code> to <code>false</code> . Encoders and in
particular <a
href="encoders.html#LayoutWrappingEncoder"><code>LayoutWrappingEncoder</code></a>
are described in a separate chapter.</p>
class="prop">immediateFlush</span> property to
<code>false</code>.</p>

<p>Below is an example of a configuration file for
<code>FileAppender</code>:
Expand All @@ -528,6 +541,8 @@ <h2 class="doAnchor" name="FileAppender">FileAppender</h2>
<b>&lt;appender name="FILE" class="ch.qos.logback.core.FileAppender">
&lt;file>testFile.log&lt;/file>
&lt;append>true&lt;/append>
&lt;!-- set immediateFlush to false for much higher logging throughput -->
&lt;immediateFlush>true&lt;/immediateFlush>
&lt;!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
&lt;encoder>
Expand Down
55 changes: 9 additions & 46 deletions logback-site/src/site/pages/manual/encoders.html
Original file line number Diff line number Diff line change
Expand Up @@ -142,13 +142,11 @@ <h2 class="doAnchor">LayoutWrappingEncoder</h2>

protected Layout&lt;E> layout;
private Charset charset;
private boolean immediateFlush = true;

public void doEncode(E event) throws IOException {
String txt = layout.doLayout(event);
outputStream.write(convertToBytes(txt));
if (immediateFlush)
outputStream.flush();

// encode a given event as a byte[]
public byte[] encode(E event) {
String txt = layout.doLayout(event);
return convertToBytes(txt);
}

private byte[] convertToBytes(String s) {
Expand All @@ -163,15 +161,7 @@ <h2 class="doAnchor">LayoutWrappingEncoder</h2>
<p>The <code>doEncode</code>() method starts by having the wrapped
layout convert the incoming event into string. The resulting text
string is converted to bytes according to the charset encoding
chosen by the user. Those bytes are then written to the
<code>OutputStream</code> given by the owning appender. By
default, the <code>OutputStream</code> is immediately flushed,
unless the <span class="prop">immediateFlush</span> property is
explicitly set to 'false'. Setting the <span
class="prop">immediateFlush</span> property to false can
significantly improve logging throughput. See
<code>PatternLayoutEncoder</code> below for sample configuration..
</p>
chosen by the user.</p>


<h2 class="doAnchor">PatternLayoutEncoder</h2>
Expand All @@ -194,36 +184,9 @@ <h2 class="doAnchor">PatternLayoutEncoder</h2>

<h4 class="doAnchor" name="immediateFlush"><span class="prop">immediateFlush</span> property</h4>

<p>As a sub-class of <a
href="../xref/ch/qos/logback/core/encoder/LayoutWrappingEncoder.html"><code>LayoutWrappingEncoder</code></a>,
<code>PatternLayoutEncoder</code> admits the <span
class="prop">immediateFlush</span> property. The default value
for <span class="prop">immediateFlush</span> is 'true'. Immediate
flushing of the output stream ensures that logging events are
immediately written to disk and will not be lost in case your
application exits without properly closing appenders. On the
other hand, setting this property to 'false' is likely to
quintuple (your mileage may vary) logging throughput. As
mentioned previously, if <span class="prop">immediateFlush</span>
is set to 'false' and if appenders are not closed properly when
your application exits, then logging events not yet written to
disk may be lost.
</p>

<p>Below is a sample configuration for a
<code>FileAppender</code> containing a
<code>PatternLayoutEncoder</code> with its <span
class="prop">immediateFlush</span> property set to 'false'.</p>

<pre class="prettyprint">&lt;appender name="FILE" class="ch.qos.logback.core.FileAppender">
&lt;file>foo.log&lt;/file>
&lt;encoder>
&lt;pattern>%d %-5level [%thread] %logger{0}: %msg%n&lt;/pattern>
&lt;!-- this quadruples logging throughput --&gt;
<b>&lt;immediateFlush>false&lt;/immediateFlush></b>
&lt;/encoder>
&lt;/appender></pre>

<p>As of <span class="label notice">logback 1.2.0</span>, the
<span class="prop">immediateFlush</span> property is part of the
enclosing Appender.</p>

<h4 class="doAnchor" name="outputPatternAsHeader">Output pattern
string as header</h4>
Expand Down

0 comments on commit d3d704c

Please sign in to comment.