[英]Need more info when logging servlet exceptions through email
環境
我正在運行許多使用Servlet的應用程序,包括基於JSF和JAX-WS的應用程序以及一些自己的自定義Servlet。 我正在使用Tomcat 7.x作為我的Web容器。 我正在使用java.util.logging記錄消息。
現在的情況
對於記錄異常,我一直在使用SMTPHandler ,該方法效果很好。 以下是我的logging.properties文件的相關摘錄:
handlers = {... other handlers ...},08SMTP.smtphandler.SMTPHandler
08SMTP.smtphandler.SMTPHandler.level=SEVERE
08SMTP.smtphandler.SMTPHandler.smtpHost=smtp.somedomain.com
08SMTP.smtphandler.SMTPHandler.to=developers@somedomain.com
08SMTP.smtphandler.SMTPHandler.from=developers@somedomain.com
08SMTP.smtphandler.SMTPHandler.subject=MyApplication error message
08SMTP.smtphandler.SMTPHandler.bufferSize=512
08SMTP.smtphandler.SMTPHandler.formatter=java.util.logging.SimpleFormatter
此設置的唯一問題是電子郵件僅包含一個例外。 沒有關於錯誤發生的上下文的其他信息。
我想看到的
我希望電子郵件包含來自ServletRequest
/ HttpServletRequest
對象的其他上下文信息,例如:
嘗試的解決方案
由logging.properties
文件配置的Logging Handler
只能通過靜態變量訪問應用程序的其他部分,因此我認為我將嘗試以編程方式創建Logging Handler
。 我試圖創建一個處理程序,但是沒有辦法讓它知道異常發生時處於活動狀態的HttpServletRequest
。
我試圖創建自己的類,該類同時實現ServletRequestListener
和ServletContextListener
,然后注冊一個了解ThreadLocal<ServletRequest>
變量的自定義日志記錄Handler
,然后在ServletRequestListener
設置並清除該ThreadLocal
變量。 在我的web.xml
文件中添加了一個<listener>
引用,該引用正確地調用了contextInitialized
和requestInitialized
, 當發生異常時, 永遠不會調用日志記錄處理程序的publish
方法 。
代碼在這里。
public class LoggingWebListener
implements ServletRequestListener, ServletContextListener
{
public static class FtSmtpHandler
extends Handler
{
private final ServletContext sc;
private final ThreadLocal<ServletRequest> servletReqLocal;
public FtSmtpHandler(ServletContext servletContext, ThreadLocal<ServletRequest> servletReqLocal)
{
this.sc = servletContext;
this.servletReqLocal = servletReqLocal;
}
@Override public void publish(LogRecord record)
{
if (record.getLevel().intValue() < Level.WARNING.intValue())
return;
// Don't try to send email if the emailer fails and logs an exception
if (record.getLoggerName().equals(MyEmailHelper.class.getName()))
return;
// CODE TO SEND EMAIL GOES HERE
}
@Override public void flush()
{
}
@Override public void close()
throws SecurityException
{
}
}
public static final Logger glogger = Logger.getGlobal();
public static final Logger logger = Logger.getLogger(LoggingWebListener.class.getName());
private final ThreadLocal<ServletRequest> servletReqLocal = new ThreadLocal<>();
private FtSmtpHandler handler;
public LoggingWebListener()
{
}
@Override public void contextInitialized(ServletContextEvent evt)
{
logger.log(Level.INFO, "Initializing context for " + getClass().getName());
ServletContext servletContext = evt.getServletContext();
handler = new FtSmtpHandler(servletContext, servletReqLocal);
glogger.addHandler(handler);
}
@Override public void contextDestroyed(ServletContextEvent arg0)
{
glogger.removeHandler(handler);
handler = null;
}
@Override public void requestInitialized(ServletRequestEvent evt)
{
ServletRequest servletRequest = evt.getServletRequest();
// logger.log(Level.INFO, "Initializing request for request " + servletRequest);
servletReqLocal.set(servletRequest);
}
@Override public void requestDestroyed(ServletRequestEvent evt)
{
servletReqLocal.remove();
}
}
我的工作有小錯誤嗎? 這是完全錯誤的方法嗎? 是否有一個已經存在的模塊可以執行我尚未找到的所需功能? 還有另一種方法可以做我想做的事嗎?
這篇文章提出了一種與我所采取的方法類似的方法,但是沒有細節。
創建一個自定義servlet過濾器,該過濾器將為應用程序的所有調用觸發。 然后創建一個自定義格式化程序,該格式化程序知道如何格式化請求的屬性。 在過濾器內部,捕獲當前請求,並將其發送到安裝在SMTPHandler上的自定義格式化程序,以獲取對請求對象的訪問權限。
public class RequestContextFilter implements javax.servlet.Filter {
private static final String CLASS_NAME = MdcFilter.class.getName();
private static final Logger logger = Logger.getLogger("");
private volatile Handler emailer;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
emailer = new SMTPHandler();
//etc...
emailer.setFormatter(new ContextFormatter());
logger.addHandler(emailer);
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
ContextFormatter.CTX.set(request);
try {
chain.doFilter(request, response);
} finally {
ContextFormatter.CTX.remove();
}
}
@Override
public void destroy() {
logger.removeHandler(emailer);
emailer.close();
}
private static class ContextFormatter extends Formatter {
static final ThreadLocal<ServletRequest> CTX = new ThreadLocal();
private final Formatter txt = new SimpleFormatter();
@Override
public String format(LogRecord record) {
HttpServletRequest req = (HttpServletRequest) CTX.get();
return req.getRequestURI() + " " + txt.format(record);
}
}
}
由於這使用的是本地線程,因此如果記錄器和過濾器之間存在線程切換,則將無法使用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.