<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD>
<META content="text/html; charset=utf-8" http-equiv=Content-Type>
<STYLE>
BLOCKQUOTE {
MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px; MARGIN-LEFT: 2em
}
OL {
MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px
}
UL {
MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px
}
DIV.FoxDiv20140307144001643047 {
COLOR: #000000
}
P {
MARGIN-TOP: 0px; MARGIN-BOTTOM: 0px
}
BODY {
LINE-HEIGHT: 1.5; FONT-FAMILY: 微软雅黑; COLOR: #000000; FONT-SIZE: 10.5pt
}
</STYLE>
<META name=GENERATOR content="MSHTML 9.00.8112.16533"></HEAD>
<BODY style="MARGIN: 10px">
<DIV>I've writen a faster AsyncAppender which use disruptor,and run
stable in my project for years.the source is at <A
href="https://github.com/cp149/jactor-logger">https://github.com/cp149/jactor-logger</A>,
Maybe it can give you some help.</DIV>
<DIV> </DIV>
<HR style="WIDTH: 210px; HEIGHT: 1px" align=left color=#b5c4df SIZE=1>
<DIV><SPAN>champion</SPAN></DIV>
<DIV> </DIV>
<DIV
style="BORDER-BOTTOM: medium none; BORDER-LEFT: medium none; PADDING-BOTTOM: 0cm; PADDING-LEFT: 0cm; PADDING-RIGHT: 0cm; BORDER-TOP: #b5c4df 1pt solid; BORDER-RIGHT: medium none; PADDING-TOP: 3pt">
<DIV
style="PADDING-BOTTOM: 8px; PADDING-LEFT: 8px; PADDING-RIGHT: 8px; FONT-FAMILY: tahoma; BACKGROUND: #efefef; COLOR: #000000; FONT-SIZE: 12px; PADDING-TOP: 8px">
<DIV><B>From:</B> <A href="mailto:mike@coursescheduler.io">Michael
Reinhold</A></DIV>
<DIV><B>Date:</B> 2014-03-07 02:48</DIV>
<DIV><B>To:</B> <A href="mailto:logback-user@qos.ch">logback users
list</A></DIV>
<DIV><B>Subject:</B> Re: [logback-user] AsyncAppenderBase not flushing
queue during JVM shutdown</DIV></DIV></DIV>
<DIV>
<DIV style="BACKGROUND-COLOR: white" class=FoxDiv20140307144001643047>
<DIV dir=ltr>Hi David, Chris,
<DIV><BR></DIV>
<DIV>Thanks for the feedback. In the test case that I ran with the
AsyncAppender, LoggerContext.stop did not wait for the async appender to finish
flushing before exiting - the stop method returned immediately. I just retested
this scenario to be sure. My test should generate exactly 135 log events (also
going to a local file appender, so I can confirm that entries are present or
missing). Without the async appender I get all of the expected events (at much
lower throughput); with the async appender (using the call to LoggerContext.stop
prior to exiting) I get a variable number (generally less than 20 events). It's
possible that the lower latency of flushing to local files results in the async
appender being able to flush completely to disk before the JVM exits, whereas
the latency to Loggly is just too high. Queue size probably also has an impact -
I allow for a fairly large queue because the penalty of falling back to
synchronous logging for Loggly is large and my logging tends to come in
bursts.</DIV>
<DIV><BR></DIV>
<DIV>If stop() did wait for async appenders to flush, that would improve the
situation. Although the use-case that involves shutdown hooks that want to log
would still need to be addressed. The only way that I can see that does address
allowing shutdown hooks to log is via a queue flushing shutdown hook (requiring
a call to LoggerContext.stop won't help for shutdown hooks or applications that
use JVM implicit shutdowns). My opinion is that a shutdown hook can satisfy both
needs with minimal impact to other classes or semantics on how
LoggerContext.stop functions. One hook per async appender would be simple to
implement and would also allow for parallel flushing of async appender
queues.<BR></DIV>
<DIV><BR></DIV>
<DIV>A default shutdown timeout as you described makes sense to me and fits with
what I was thinking for implementation of a shutdown hook.</DIV>
<DIV><BR></DIV>
<DIV>I could probably throw together a quick and dirty implementation tonight as
a proof of concept. Thoughts?</DIV>
<DIV><BR></DIV>
<DIV>Regards,</DIV>
<DIV><BR></DIV>
<DIV>Mike Reinhold</DIV>
<DIV><BR></DIV></DIV>
<DIV class=gmail_extra><BR><BR>
<DIV class=gmail_quote>On Thu, Mar 6, 2014 at 1:01 PM, Chris Pratt <SPAN
dir=ltr><<A href="mailto:thechrispratt@gmail.com"
target=_blank>thechrispratt@gmail.com</A>></SPAN> wrote:<BR>
<BLOCKQUOTE
style="BORDER-LEFT: #ccc 1px solid; MARGIN: 0px 0px 0px 0.8ex; PADDING-LEFT: 1ex"
class=gmail_quote>
<DIV dir=ltr>Another option might be to require calling the LoggerContext.stop
when using AsyncAppender's. The stop method could signal to the
AsyncAppenders that they should drain their queue's and exit, which would
allow the application to shut down naturally (if the AsyncAppenders aren't
started as demon's)<SPAN class=HOEnZb><FONT color=#888888>
<DIV> (*Chris*)</DIV></FONT></SPAN></DIV>
<DIV class=HOEnZb>
<DIV class=h5>
<DIV class=gmail_extra><BR><BR>
<DIV class=gmail_quote>On Thu, Mar 6, 2014 at 9:55 AM, David Roussel <SPAN
dir=ltr><<A href="mailto:nabble@diroussel.xsmail.com"
target=_blank>nabble@diroussel.xsmail.com</A>></SPAN> wrote:<BR>
<BLOCKQUOTE
style="BORDER-LEFT: #ccc 1px solid; MARGIN: 0px 0px 0px 0.8ex; PADDING-LEFT: 1ex"
class=gmail_quote>
<DIV>
<DIV>Mike,</DIV>
<DIV><BR></DIV>
<DIV>I would expect the shutdown of logback to wait for async appenders to
drain their queues before we can consider the shutdown down. </DIV>
<DIV><BR></DIV>
<DIV>I have used async appenders to provide faster logging when writing to
local files. And that's what I expected happens. </DIV>
<DIV><BR></DIV>
<DIV>Of course there is the problem of waiting for too long. How about an
async appender has a default shutdown timeout which can be overridden by
config. So it can wait 2 seconds by default, but can be overridden to 30
seconds for the loggly usecase. </DIV>
<DIV><BR></DIV>
<DIV>The simple solution is to put this all in AsyncAppender(Base), but if
we want to allow multiple async appenders to shutdown in parallel, then the
fix might involve more changes. <SPAN><FONT
color=#888888><BR><BR>David</FONT></SPAN></DIV>
<DIV>
<DIV>
<DIV><BR>On 6 Mar 2014, at 12:51, Michael Reinhold <<A
href="mailto:mike@coursescheduler.io"
target=_blank>mike@coursescheduler.io</A>> wrote:<BR><BR></DIV>
<BLOCKQUOTE type="cite">
<DIV>
<DIV dir=ltr>Hi David,
<DIV><BR></DIV>
<DIV>I forgot to mention this in my original note and it is a good
thought, but yes I have tried to cleanly shutdown Logback. Even when the
LogbackContext stop method is invoked prior to JVM exit, the async queue
is not flushed completely. When Logback is stopped cleanly, the async
worker thread exits the "waiting for events in the queue" while loop and
moves on to the "flush remaining elements from the queue" for loop
(confirmed by the info message when debug is enabled and via breakpoint).
Ultimately, because the thread is a daemon thread - this process is still
interrupted by JVM shutdown. There is no guarantee that the async worker
will get enough time to process all events.</DIV>
<DIV><BR></DIV>
<DIV>From a theoretical standpoint, stopping Logback properly does not
solve flushing issues with the AsycAppender in a number of cases (it may
be fair to argue that it should not attempt to cover all of these
cases):</DIV>
<DIV>
<UL>
<LI>Queue is relatively full or wrapped appender latency is high - the
async worker may not be able to finish processing the queued items
between when the LogbackContext.stop method is invoked and when the JVM
actually exits
<LI>Implicit JVM exit upon end of last user thread - some applications
don't explicitly call System.exit but rather rely on implicit JVM exit.
In that case, where would you cleanly shutdown Logback? It can probably
be done, but would require an application redesign to avoid reliance on
implicit exit or some form of thread tracking thread.
<LI>Shutdown hooks are used to clean up resources - some applications
have components or modules that need to clean up after themselves when
the JVM shuts down. Typically, shutdown hooks are used for this purpose,
however the AsyncAppender's worker thread is unable to process queued
events after shutdown has been initiated (while the shutdown hooks are
running). This also prevents shutdown hooks from being able to log
events
<LI>Signal handling on *nix systems - signals can be sent to the
application for a number of reasons, most often to request graceful
shutdown or to terminate something that is non-responsive. If the signal
corresponds to something that generally means "graceful shutdown" on the
host system, normal cleanup routines should execute in the application
(typically implemented as shutdown hooks, so maybe this is the same as
the previous one). </LI></UL>
<DIV>From a practical standpoint, I am running into the first and the
third scenarios. Even shutting down cleanly, the latency and queue depth
can cause elements to be missed; additionally, I have shutdown hooks that
clean up resources and expect the ability to log events and
errors.</DIV></DIV>
<DIV><BR></DIV>
<DIV>My thought is that a modified AsyncAppenderBase implementation could
(optionally) install a shutdown hook that continues watching the queue for
new events. A configurable "queue idle time" could be used to tell the
shutdown hook that it has probably caught all events and can safely
shutdown (not foolproof, I'm still thinking about other ways of handling
it). Additionally, the hook could have a configurable "max processing
time" so that it doesn't keep the JVM alive for an undesirable amount of
time (thinking in the context of automation software where shutdown
requests typically have a max execution time before they are considered as
"failed online").</DIV>
<DIV><BR></DIV>
<DIV>I hope this helps clarify the scenario I am considering and a
proposed solution!</DIV>
<DIV><BR></DIV>
<DIV>Regards,</DIV>
<DIV><BR></DIV>
<DIV>Mike Reinhold</DIV></DIV>
<DIV class=gmail_extra><BR><BR>
<DIV class=gmail_quote>On Wed, Mar 5, 2014 at 5:30 PM, David Roussel <SPAN
dir=ltr><<A href="mailto:nabble@diroussel.xsmail.com"
target=_blank>nabble@diroussel.xsmail.com</A>></SPAN> wrote:<BR>
<BLOCKQUOTE
style="BORDER-LEFT: #ccc 1px solid; MARGIN: 0px 0px 0px 0.8ex; PADDING-LEFT: 1ex"
class=gmail_quote>
<DIV>
<DIV>Did you try shutting down logback cleanly. Like this <A
href="http://stackoverflow.com/questions/3678755/do-i-need-to-flush-events-when-shutting-down-using-logback"
target=_blank>http://stackoverflow.com/questions/3678755/do-i-need-to-flush-events-when-shutting-down-using-logback</A><BR><BR>David</DIV>
<DIV>
<DIV>
<DIV><BR>On 5 Mar 2014, at 20:44, Michael Reinhold <<A
href="mailto:mike@coursescheduler.io"
target=_blank>mike@coursescheduler.io</A>> wrote:<BR><BR></DIV>
<BLOCKQUOTE type="cite">
<DIV>
<DIV dir=ltr>
<DIV dir=ltr>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px">Hi
Ceki, </DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px"><BR></DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px">I am
currently using the AsyncAppender in combination with the
LogglyAppender from the Logback extensions project. While working on a
number of aspects of my application, I found that I was not
consistently getting all of the log messages that I expected. In
particular, when the application shuts down immediately (or very
shortly) after a burst of logging activity, the tail of those log
events is often not present in Loggly. From a number of tests, this
issue is not restricted to use with the LogglyAppender, but is simply
more evident because of the latency involved.</DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px"><BR></DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px">Looking
through the source code for the AsyncAppenderBase, I saw the
following:</DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px"><BR></DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px">You create
the Async sender thread as a Daemon thread</DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px"><BR></DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px"><BR></DIV>
<DIV
style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px">addInfo("Setting
discardingThreshold to " + discardingThreshold);</DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px">
worker.setDaemon(true);</DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px">
worker.setName("AsyncAppender-Worker-" +
worker.getName());</DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px">
// make sure this instance is marked as "started" before
staring the worker Thread</DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px">
super.start();</DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px">
worker.start();</DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px"><BR></DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px"><BR></DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px">In the
sender thread, if you determine that the parent thread has stopped or
the async sender thread has been interrupted, you allow the worker
thread to flush remaining log events in the queue.</DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px"><BR></DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px"><BR></DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px">while
(parent.isStarted()) {</DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px">
try {</DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px">
E e = parent.blockingQueue.take();</DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px">
aai.appendLoopOnAppenders(e);</DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px">
} catch (InterruptedException ie) {</DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px">
break;</DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px">
}</DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px">
}</DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px"><BR></DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px">
addInfo("Worker thread will flush remaining events
before exiting. ");</DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px">
for (E e : parent.blockingQueue) {</DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px">
aai.appendLoopOnAppenders(e);</DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px">
}</DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px"><BR></DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px">
aai.detachAndStopAllAppenders();</DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px"><BR></DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px"><BR></DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px">From what
I can tell, during JVM shutdown (for a standalone SE instance, the
same is probably not true for EE instances with application servers)
daemon threads may be terminated without allowing the the
AsyncAppenderBase to escape the while loop and proceed onto the queue
flush for loop. </DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px"><BR></DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px">From Brian
Goetz in Java Concurrency in Practice:</DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px">"When a
thread exits, the JVM performs an inventory of running threads, and if
the only threads that are left are daemon threads, it initiates an
orderly shutdown. When the JVM halts, any remaining daemon threads are
abandoned finally blocks are not executed, stacks are not unwound the
JVM just exits. Daemon threads should be used sparingly few processing
activities can be safely abandoned at any time with no cleanup. In
particular, it is dangerous to use daemon threads for tasks that might
perform any sort of I/O. Daemon threads are best saved for
"housekeeping" tasks, such as a background thread that periodically
removes expired entries from an in-memory cache."</DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px"><BR></DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px">To test
this, I inserted a break point in the AsyncAppenderBase at the call to
addInfo and ran a variety of different scenarios. At no point in time
was I able to get the breakpoint to stop at the addInfo
line. </DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px"><BR></DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px">I don't
think there are any clear cut solutions to this. Making the worker
thread a user thread instead of daemon thread has its own
implications, particularly that if all other user threads have exited
the async threads would keep the JVM instance alive (unless
System.exit has been called, in which case I believe that you will
still have lost log events even if the async processing thread is not
a daemon). It might be possible to create a shutdown hook that does
the queue flushing for the async worker - though you may need to
consider the possibility of other shutdown hooks wanting to log events
as well.</DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px"><BR></DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px">I'm
currently mocking up a version of the AsyncAppenderBase and
AsyncAppender that installs a shutdown hook as described previously. I
think a "queue idle time" configuration might be the best way to
handle other shutdown hooks adding log events (aka - after processing
whatever was in the queue, if no new events are added
within <I><B>x</B></I> ms then the shutdown hook can assume
nothing else will be adding log events and can exit). An
alternative might be to have the shutdown hook query the ThreadMXBean
API to determine if other shutdown hooks are still executing and
possibly adding log events (though the threads that are expected to be
running during shutdown may be not only application specific but also
JVM implementation specific... I'm not sure).</DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px"><BR></DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px">Let me
know what you think. I'll let you know if I feel that my mockup may be
viable...</DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px"><BR></DIV>
<DIV
style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px">Regards,</DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px"><BR></DIV>
<DIV style="FONT-FAMILY: arial,sans-serif; FONT-SIZE: 13px">Mike
Reinhold</DIV>
<DIV><BR></DIV></DIV></DIV></DIV></BLOCKQUOTE></DIV></DIV>
<BLOCKQUOTE type="cite">
<DIV><SPAN>_______________________________________________</SPAN><BR><SPAN>Logback-user
mailing list</SPAN><BR><SPAN><A href="mailto:Logback-user@qos.ch"
target=_blank>Logback-user@qos.ch</A></SPAN><BR><SPAN><A
href="http://mailman.qos.ch/mailman/listinfo/logback-user"
target=_blank>http://mailman.qos.ch/mailman/listinfo/logback-user</A></SPAN></DIV></BLOCKQUOTE></DIV><BR>_______________________________________________<BR>Logback-user
mailing list<BR><A href="mailto:Logback-user@qos.ch"
target=_blank>Logback-user@qos.ch</A><BR><A
href="http://mailman.qos.ch/mailman/listinfo/logback-user"
target=_blank>http://mailman.qos.ch/mailman/listinfo/logback-user</A><BR></BLOCKQUOTE></DIV><BR></DIV></DIV></BLOCKQUOTE>
<BLOCKQUOTE type="cite">
<DIV><SPAN>_______________________________________________</SPAN><BR><SPAN>Logback-user
mailing list</SPAN><BR><SPAN><A href="mailto:Logback-user@qos.ch"
target=_blank>Logback-user@qos.ch</A></SPAN><BR><SPAN><A
href="http://mailman.qos.ch/mailman/listinfo/logback-user"
target=_blank>http://mailman.qos.ch/mailman/listinfo/logback-user</A></SPAN></DIV></BLOCKQUOTE></DIV></DIV></DIV><BR>_______________________________________________<BR>Logback-user
mailing list<BR><A href="mailto:Logback-user@qos.ch"
target=_blank>Logback-user@qos.ch</A><BR><A
href="http://mailman.qos.ch/mailman/listinfo/logback-user"
target=_blank>http://mailman.qos.ch/mailman/listinfo/logback-user</A><BR></BLOCKQUOTE></DIV><BR></DIV></DIV></DIV><BR>_______________________________________________<BR>Logback-user
mailing list<BR><A
href="mailto:Logback-user@qos.ch">Logback-user@qos.ch</A><BR><A
href="http://mailman.qos.ch/mailman/listinfo/logback-user"
target=_blank>http://mailman.qos.ch/mailman/listinfo/logback-user</A><BR></BLOCKQUOTE></DIV><BR></DIV></DIV></DIV></BODY></HTML>