簡體   English   中英

通過電子郵件記錄servlet異常時需要更多信息

[英]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對象的其他上下文信息,例如:

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

嘗試的解決方案

logging.properties文件配置的Logging Handler只能通過靜態變量訪問應用程序的其他部分,因此我認為我將嘗試以編程方式創建Logging Handler 我試圖創建一個處理程序,但是沒有辦法讓它知道異常發生時處於活動狀態的HttpServletRequest

我試圖創建自己的類,該類同時實現ServletRequestListenerServletContextListener ,然后注冊一個了解ThreadLocal<ServletRequest>變量的自定義日志記錄Handler ,然后在ServletRequestListener設置並清除該ThreadLocal變量。 在我的web.xml文件中添加了一個<listener>引用,該引用正確地調用了contextInitializedrequestInitialized當發生異常時, 永遠不會調用日志記錄處理程序的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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM