How to redirect log4j output to HttpServletResponse output stream?

I am using log4j 1.2.15 in a Spring 3.1.1.RELEASE application deployed to JBoss AS 7.1.1.Final. I am trying to redirect the output written in log4j to the response output stream. I have a conclusion written as follows

private static final Logger LOG = Logger.getLogger(TrainingSessionServiceImpl.class); … LOG.info("Creating/updating training session associated with order #:" + order.getId()); 

and I'm trying to redirect it to the output stream this way ...

 @RequestMapping(value = "/refreshPd", method = RequestMethod.GET) public void refreshPD(final HttpServletResponse response) throws IOException { ... final WriterAppender appender = new WriterAppender(new PatternLayout("%d{ISO8601} %p - %m%n"),response.getWriter()); appender.setName("CONSOLE_APPENDER"); appender.setThreshold(org.apache.log4j.Level.DEBUG); Logger.getRootLogger().addAppender(appender); worker.work(); Logger.getRootLogger().removeAppender("CONSOLE_APPENDER"); 

but, unfortunately, nothing displays my browser, although I know (through debugging) that a logging request is being called. Does anyone know how I can configure my installation to make it work? Below is my log4j.properties file deployed in my WAR WEB-INF / classes directory.

 log4j.rootLogger=DEBUG, CA, FA #Console Appender log4j.appender.CA=org.apache.log4j.ConsoleAppender log4j.appender.CA.layout=org.apache.log4j.PatternLayout log4j.appender.CA.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n #File Appender log4j.appender.FA=org.apache.log4j.FileAppender log4j.appender.FA.File=/usr/java/jboss/server/default/log/log4j.log log4j.appender.FA.layout=org.apache.log4j.PatternLayout log4j.appender.FA.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n # Set the logger level of File Appender to WARN log4j.appender.FA.Threshold = DEBUG 

Thanks - Dave

+6
source share
4 answers

This was an interesting problem. The main thing is to write your own appender . I was looking for the inline code org.apache.log4j.ConsoleAppender for inspiration. I checked this in my tomcat and confirmed that it works. I used log4j-1.2.17 (hopefully it doesn't matter)

1) First, implement your own appender. This appender will write all log events to the current stream output.

 package com.tstwbprj.log; import org.apache.log4j.Layout; import org.apache.log4j.WriterAppender; import java.io.IOException; import java.io.OutputStream; public class HttpLogAppender extends WriterAppender { static ThreadLocal<OutputStream> streamPerHttpThread = new ThreadLocal<OutputStream>(); public HttpLogAppender() { } public HttpLogAppender(Layout layout) { setLayout(layout); //super-class method activateOptions(); } public void setCurrentHttpStream(OutputStream stream) { streamPerHttpThread.set(stream); } public void activateOptions() { setWriter(createWriter(new CurrentHttpThreadOutStream())); } /** * An implementation of OutputStream that redirects to the * current http threads servlet output stream */ private static class CurrentHttpThreadOutStream extends OutputStream { public CurrentHttpThreadOutStream() { } public void close() { } public void flush() throws IOException { OutputStream stream = streamPerHttpThread.get(); if (stream != null) { stream.flush(); } } public void write(final byte[] b) throws IOException { OutputStream stream = streamPerHttpThread.get(); if (stream != null) { stream.write(b); } } public void write(final byte[] b, final int off, final int len) throws IOException { OutputStream stream = streamPerHttpThread.get(); if (stream != null) { stream.write(b, off, len); } } public void write(final int b) throws IOException { OutputStream stream = streamPerHttpThread.get(); if (stream != null) { stream.write(b); } } } } 

2) Add this appender to the log4j configuration file in the same way as other settings

log4j.rootLogger = DEBUG, CA, FA, HA
..
log4j.appender.HA = com.tstwbprj.log.HttpLogAppender log4j.appender.HA.layout = org.apache.log4j.PatternLayout log4j.appender.HA.layout.ConversionPattern =% - 4r [% t]% -5p% c % x -% m% n

3) Add a small piece of code to your servlet so that this appender works correctly. Here is my servlet.

 import org.apache.log4j.Category; import org.apache.log4j.Logger; import javax.servlet.ServletOutputStream; import java.io.IOException; public class LogServlet extends javax.servlet.http.HttpServlet { private static final Logger LOG = Logger.getLogger(LogServlet.class); protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException { } protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException { ServletOutputStream outstream = response.getOutputStream(); configureLogForCurrentRequest(outstream); LOG.info("Got request");//this is now send to the servlet output stream !! LOG.info("Hello!!"); LOG.info("Done!!"); } private void configureLogForCurrentRequest(ServletOutputStream outstream) { HttpLogAppender appender = (HttpLogAppender) LOG.getAppender("HA"); while (appender == null) { Category parent = LOG.getParent(); if (parent == null) { break; //This ideally shouldn't happen. Navigated all the way to root logger and still did not find appender !!..something wrong with log4j configuration setup } appender = (HttpLogAppender) parent.getAppender("HA"); } appender.setCurrentHttpStream(outstream); } } 

Caution This has not been tested thoroughly, especially with multiple servlet requests, etc. Also do not know why you want to do this. This is not typical for protocol log messages in a browser. Be careful .. :) -

+7
source

Try something like this:

 Logger logger = Logger.getRootLogger(); String name = "myAppender"; Appender servletAppender = logger.getAppender(appenderName); OutputStream out = response.getOutputStream(); if (servletAppender == null) { servletAppender = new WriterAppender(new PatternLayout("%d{ISO8601} %p - %m%n"), out); servletAppender.setName(appenderName); appender.setThreshold(org.apache.log4j.Level.DEBUG); logger.addAppender(servletAppender); } try { // Your work worker.work(); } finally { logger.removeAppender(appenderName); out.flush(); } 
+1
source

I suggest using an alternative approach and getting the contents of the log file for a separate browser tab.

This does not require modification of the main code and will not destroy the original page formatting.

Some web links for viewing log files:

0
source

Not the exact answer as such, but the best way I've seen this is to write your own Appender that will collect logs in ThreadLocal . At the time your servlet request is complete, you can merge the contents of ThreadLocal and output it to the response stream as you wish.

This satisfies the (non-stationary) requirement of thread safety and can quite clearly isolate the log4j implementation code (or other logging code) (which should be small using this technique) from manipulating ThreadLocal , which could theoretically be used in other areas of your code.

This type of technique is used by many server-side scripts such as ColdFusion and others.

I will not go into potential errors that may occur if ThreadLocal used improperly on the application server, there are methods for managing this, as well as corresponding answers on SO and other sites.

Hope this answer can redirect your thinking in a slightly different direction!

0
source

All Articles