2015-10-28 233 views
0

環境通過電子郵件

我跑了許多使用Servlet的,包括基於JSF和JAX-WS和我自己的一些定製的Servlet的那些應用程序登錄的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 
[email protected] 
[email protected] 
08SMTP.smtphandler.SMTPHandler.subject=MyApplication error message 
08SMTP.smtphandler.SMTPHandler.bufferSize=512 
08SMTP.smtphandler.SMTPHandler.formatter=java.util.logging.SimpleFormatter 

此設置的唯一問題是電子郵件只包含一個例外。沒有關於發生錯誤的上下文的其他信息。

我想看到的

我想的電子郵件包含從ServletRequest/HttpServletRequest對象的其它背景信息,如:

  • 誰是登錄的用戶在?
  • 什麼是請求的queryString,URL,URI,ContextPath,ServletPath和getMethod?
  • 什麼是標題參數?
  • 參數是什麼?
  • 什麼是屬性名稱/值?

的嘗試性解決方案

記錄Handler s數logging.properties文件配置沒有訪問除通過靜態變量的應用程序的其他部分,所以我想我會嘗試創建一個記錄Handler以編程方式。我試圖製作一個處理程序,但是沒有辦法讓它知道在異常時處於活動狀態的HttpServletRequest

我試圖創建自己的類,同時實現了ServletRequestListenerServletContextListener,然後註冊一個自定義日誌Handler它知道一個ThreadLocal<ServletRequest>變量,然後設置並在ServletRequestListener明確表示ThreadLocal變量。在我web.xml文件,該文件正確調用contextInitializedrequestInitialized我的日誌Handler的publish方法添加<listener>引用後,當發生異常不會被調用

這裏的代碼就在這裏。

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

我在做什麼有一個小錯誤?這是完全錯誤的方法嗎?有沒有一個已經存在的模塊能夠做我想要的,我還沒有找到的模塊?有另一種方法可以做我想做的事嗎?

This post建議了一種類似於我所採取的方法,但沒有詳細信息。

回答

1

創建一個定製的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); 
      } 
     } 
    } 

由於這是使用本地線程,如果記錄器和過濾器之間存在線程切換,它將不起作用。

+0

下面是一個工作示例!事實證明,無論是我原來的ServletRequestListener,ServletContextListener方法還是您的servlet Filter方法都可以工作。你的例子有2個關鍵的改進,我的嘗試失敗了:1.使用Logger.getLogger(「」)而不是Logger.getGlobal(); 2.一定要在'Handler'上設置'Formatter'(我試圖使用它在「//代碼發送電子郵件到這裏」部分)!你爲子類化一個新的'Formatter'的方法也是一種優越的方法,我將利用它。周圍的好東西,我學到了很多,非常感謝! – Pixelstix