It would have helped if you had provided the complete handler implementation code, and not just the doAfterBody() method, but I think I've guessed correctly what you mean...
The buffer is a BodyContent instance. When using the buffer (signalled by returning EVAL_BODY_BUFFERED from doStartTag()), the first thing the container does is invoke the pageContext.pushBody() method, which causes the current 'out' Writer (perhaps of the parent buffered BodyTag or of the JSP page itself if there is no parent buffered BodyTag) to be stored on the stack, and a new BodyContent to be returned. The push/pop stack facility is used to provide Writers distinct from the actual response stream which can be used to control the flow of content to the response.
Now, recall that for content of a buffered BodyTag to be written to the response, you need to explicitly write it using methods on BodyContent (such as writeOut(getEnclosingWriter()) which will write the entire contents to the parent's BodyContent or 'out' if there is no parent BodyContent).
1. In your code, if you return EVAL_BODY_INCLUDE from doStartTag(), then by the time you get to doAfterBody(), no BodyContent buffer exists in your handler (because you didn't return EVAL_BODY_BUFFERED). Hence, the 'out' object is the actual page's JspWriter (or the BodyContent of a parent tag which has been invoked). So, calling out.println(...) will write the contents directly to the JspWriter for the page, which is in turn written to the response (and shown in the browser).
2. If you return EVAL_BODY_BUFFERED from doStartTag(), the BodyContent buffer is initialised by calling PageContext.pushBody(). The new 'out' object returned by getOut() is the BodyContent buffer and NOT the original JspWriter for the page. So, now invoking out.println(...) writes to the BodyContent for the tag and not directly to the JspWriter for the page. BUT, since you never write the contents of the BodyContent explicitly to the page, this will never be written to the page or the response stream, so it is lost forever (which is why it appears only in the logs).
3. Now, assuming the buffer was initialised (in the 'body' variable), invoking body.getString() returns the String contents of the BodyContent buffer. You then write that explicitly to an output stream, thus flushing the buffer (containing the data you wrote in doAfterBody()) into another stream. What you haven't specified is where the 'out' variable here points to - is it the System.out or the pageContext.getOut()? I assume it is System.out, since pageContext.getOut() actually still refers to the BodyContent buffer - writing the buffer into itself isn't the best thing to do (and won't give you any output).
BodyContent.getEnclosingWriter() returns the parent JspWriter - either the parent buffered BodyTag's BodyContent, or the page's JspWriter. BodyTagSupport.getPreviousOut() is synonymous to invoking BodyContent.getEnclosingWriter().
NOTE: I've avoided a discussion of SimpleTags here. A SimpleTag has its own JspFragment buffering mechanism which is distinct from the classic model, and is not actually (and neither does contain) a JspWriter. [ December 22, 2005: Message edited by: Charles Lyons ]
Charles Lyons (SCJP 1.4, April 2003; SCJP 5, Dec 2006; SCWCD 1.4b, April 2004)
Author of OCEJWCD Study Companion for Oracle Exam 1Z0-899 (ISBN 0955160340 / AmazonAmazon UK )