2013-01-18 104 views
6

我在部署在JBoss AS 7.1.1.Final上的Spring 3.1.1.RELEASE應用程序中使用了log4j 1.2.15。我試圖將寫入log4j的輸出路由到我的響應輸出流。我已經輸出這樣寫的如何將log4j輸出重定向到我的HttpServletResponse輸出流?

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

,我想它的路線,像這樣我的輸出流...

@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"); 

,但遺憾的是,沒有什麼是讓輸出到我的瀏覽器,儘管我知道(通過調試)日誌語句被調用。有誰知道我可以如何調整我的設置以使其工作?以下是我的log4j.properties文件,部署到我的wAR的WEB-INF/classes目錄中。

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 

謝謝 - 戴夫

+0

'worker.work()'運行在相同的還是其他線程中?問題表明它在另一個線程中運行。 – BalusC

+0

是的,它運行在同一個線程中。 – Dave

+0

在Tomcat上適合我。經過一番調查,它看起來像一個JBoss AS特定問題:http://stackoverflow.com/questions/6071809/adding-log4j-appenders-programmatically,隨後https://issues.jboss.org/browse/JBAS-9318 – BalusC

回答

7

這是一個有趣的問題。 關鍵是要編寫自己的appender。我查閱了構建的org.apache.log4j.ConsoleAppender代碼以獲取靈感。我已經在我的tomcat中測試過了,並且證明它可以工作。我使用了log4j-1.2。17(希望不要緊)

1)首先實現你自己的appender。該附加器會寫所有的日誌事件,當前線程的OutputStream

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)在你的log4j的配置文件中添加此附加器就像其他設置

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)在您的servlet中添加一小段代碼,以便該appender正常工作。這是我的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); 
    } 
} 

注意:這是不是徹底尤其是與多個servlet請求等測試,而且不知道爲什麼要這麼做。它不常用於將日誌消息傳遞給瀏覽器。謹慎行事:) -

+0

嗨,我得到一個NullPointerException在行「appender.setCurrentHttpStream(outstream);」。我注意到,當「HttpLogAppender appender =(HttpLogAppender)LOG.getAppender(」HA「);」被稱爲,appender最初也是null。你想用「configureLogForCurrentRequest」做什麼? – Dave

+0

如果您的log4j.properties設置正確,那麼HttpLogAppender應該與您的根記錄器相關聯......它將在第一行中爲空。這就是爲什麼我們使用while循環來記錄器層次結構從根記錄器檢索它。一旦檢索到HttpLogAppender,我們將它傳遞給當前Servlet的響應輸出流,以便將任何日誌記錄事件寫入它 – Zenil

+1

我發現JBoss與log4j混淆比Penn St防禦協調器更糟 - http://stackoverflow.com/questions/9584787 /使用-log4j的與 - 的JBoss-7-1。一旦我添加了該部署結構文件,就可以解決問題了。謝謝, - – Dave

1

與這樣的嘗試:

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(); 
} 
+0

對於org.apache.log4j.Appender類似乎沒有「setThreshold」方法。我評論了「setThreshold」行,但仍然沒有骰子 - 沒有任何內容寫入瀏覽器。有趣的是,日誌輸出寫入我的JBoss 7.1.1服務器日誌。 – Dave

0

這不是一個確切的答案,但更好的方式,我已經看到這個處理是寫你自己的Appender將收集在ThreadLocal日誌。當您的servlet請求完成時,您可以排除ThreadLocal的內容,然後將其輸出到響應流,但是您希望。

這滿足了線程安全的(未說明的)要求,並且可以相當乾淨地隔離操作ThreadLocal的log4j(或其他日誌框架)實現代碼(應該很小,使用這種技術)理論可以在代碼的其他領域重用。

這種類型的技術被許多服務器端腳本語言(如ColdFusion等)所使用。

我不會去到潛在的bug你可以原因與應用程序服務器使用不當ThreadLocal,有技術對SO和其他網站管理這與relevant answers一起。

希望這個答案可能會改變你的想法在一個稍微不同的方向!