2013-01-18 35 views
6

Używam log4j 1.2.15 w aplikacji Spring 3.1.1.RELEASE wdrożonej na JBoss AS 7.1.1.Final. Próbuję routować dane wyjściowe zapisane w log4j do mojego strumienia wyjściowego odpowiedzi. Mam wyjście napisane jak toJak przekierować wyjście log4j do mojego strumienia wyjściowego HttpServletResponse?

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

i próbuję trasie do mojego strumienia wyjściowego jak tak ...

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

ale niestety, nic nie staje się wyjście do przeglądarce, chociaż wiem (poprzez debugowanie), że wywołania rejestrowania są wywoływane. Czy ktoś wie, jak mogę dostosować moją konfigurację, aby działała? Poniżej znajduje się mój plik log4j.properties wdrożony w katalogu WEB-INF/classes mojego wAR.

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 

Dzięki, - Dave

+0

Czy 'worker.work()' działa w tym samym lub innym wątku? Problem sugeruje, że działa on w innym wątku. – BalusC

+0

Tak, działa w tym samym wątku. – Dave

+0

Działa dobrze dla mnie na Tomcat. Po drobiazgowej analizie wygląda to jak specyficzny problem JBoss AS: http://stackoverflow.com/questions/6071809/adding-log4j-appenders- programowo, a następnie https://issues.jboss.org/browse/JBAS-9318 – BalusC

Odpowiedz

7

To był interesujący problem. Kluczową sprawą jest napisanie własnego aplikatora. Sprawdziłem zainspirowany wbudowany kod org.apache.log4j.ConsoleAppender. Przetestowałem to w moim tomcat i sprawdziłem, że działa. Użyłem log4j-1.2.17 (mam nadzieję, że nie powinno to mieć znaczenia)

1) Najpierw zaimplementuj własnego aplikanta. Ten appender zapisze wszystkie zdarzenia z dziennika do aktualnych wątku 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) Dodaj appender w pliku konfiguracyjnym log4j podobnie jak inne ustawienia

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) Dodaj niewielki fragment kodu do serwletu, aby ten appender działał poprawnie. Oto mój aplet.

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); 
    } 
} 

Uwaga: To nie jest dokładnie przetestowany szczególnie z wielu żądań serwletów itp Również nie wiem, dlaczego chcesz to zrobić. Nie jest to typowe dla komunikatów dziennika rur do przeglądarki. Kontynuuj z ostrożnością ..:) -

+0

Witam, otrzymuję wyjątek NullPointerException na linii "appender.setCurrentHttpStream (outstream);". Zauważam, że gdy "HttpLogAppender appender = (HttpLogAppender) LOG.getAppender (" HA ");" nazywa się, appender jest również początkowo pusty. Co próbujesz zrobić z "configureLogForCurrentRequest"? – Dave

+0

HttpLogAppender powinien być skojarzony z głównym dziennikiem, jeśli log4j.properties jest poprawnie skonfigurowany ... W pierwszym wierszu będzie to wartość pusta. To dlatego mamy pętlę while, aby przejść do hierarchii rejestratora, aby pobrać ją z głównego programu rejestrującego. Po pobraniu HttpLogAppender przekazujemy mu bieżący strumień wyjściowy dla obsługi serwletów, dzięki czemu zapisywane jest każde zdarzenie rejestrowania. – Zenil

+1

Odkryłem, że JBoss miesza się z log4j gorzej niż koordynator obrony Penn St. - http://stackoverflow.com/questions/9584787/using-log4j-with-jboss-7-1. Po dodaniu tego pliku struktury wdrożenia, działasz rozwiązanie. Dzięki, - – Dave

1

spróbuj coś takiego:

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

Wygląda na to, że nie istnieje metoda "setThreshold" dla klasy org.apache.log4j.Appender. Skomentowałem linię "setThreshold", ale nadal nie ma kości - nic nie jest zapisane w przeglądarce. Co ciekawe, dane wyjściowe dziennika są zapisywane w dzienniku serwera JBoss 7.1.1. – Dave

0

Nie jest to dokładna odpowiedź, ale lepszym sposobem, w jaki widziałem to, jest napisanie własnego Appendera, który zbierze logi w postaci ThreadLocal. W momencie zakończenia żądania serwletu można drenować zawartość pliku ThreadLocal i wyprowadzać do strumienia odpowiedzi, jak tylko chcesz.

To spełnia (nieokreślone) wymaganie dotyczące bezpieczeństwa wątków i może dość czysto izolować kod implementacyjny log4j (lub inny kod rejestracyjny) (który powinien być mały, używając tej techniki) od manipulowania ThreadLocal, który mógłby Teoria może być ponownie użyta w innych obszarach kodu.

Ten rodzaj techniki jest używany przez wiele języków skryptowych po stronie serwera, takich jak ColdFusion i inne.

Nie pójdę do potencjalnych błędów mogłeś powodują z niewłaściwego wykorzystania ThreadLocal w serwerze aplikacji, istnieją techniki, aby poradzić sobie z tym, wraz z relevant answers na SO i innych stron.

Mam nadzieję, że ta odpowiedź może przekierować twoje myślenie w nieco innym kierunku!