我在JBoss AS 7.1.1.Final上部署的Spring 3.1.1.RELEASE应用程序中使用log4j 1.2.15。我正在尝试将log4j中写入的输出路由到我的响应输出流。我有这样写的输出
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 :(得分:7)
这是一个有趣的问题。 关键是要编写自己的appender 。我查看了内置的org.apache.log4j.ConsoleAppender代码以获取灵感。我已经在我的tomcat中对此进行了测试并验证了它的工作原理。我使用了log4j-1.2.17(希望无所谓)
1)首先实现自己的appender。此appender会将所有日志事件写入当前线程的输出流
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)将此appender添加到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请求等,未经过彻底测试。也不确定为什么要这样做。将日志消息传递给浏览器并不常见。谨慎行事.. :) -
答案 1 :(得分: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();
}
答案 2 :(得分:0)
这不是一个准确的答案,但我看到这个处理的更好的方法是编写自己的Appender,它将收集ThreadLocal
中的日志。在您的servlet请求完成时,您可以排除ThreadLocal
的内容并输出到您希望的响应流。
这满足了线程安全的(未说明的)要求,并且可以从ThreadLocal
的操作中相当干净地隔离log4j(或其他日志框架)实现代码(使用此技术应该很小),理论上可以在代码的其他方面重复使用。
许多服务器端脚本语言(如ColdFusion和其他语言)都使用此类技术。
我不会在应用服务器中不恰当地使用ThreadLocal
来解决您可能导致的潜在错误,有一些方法可以管理此问题以及relevant answers在SO和其他网站上。
希望这个答案可能会将你的想法转向一个稍微不同的方向!