通过电子邮件

时间:2015-10-28 17:01:52

标签: java servlets tomcat7 java.util.logging

环境

我正在运行许多使用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是什么?
  • 标题参数是什么?
  • 参数是什么?
  • 属性名称/值是什么?

尝试的解决方案

Handler文件配置的logging.properties日志记录除了通过静态变量之外,无法访问应用程序的其他部分,所以我想我会尝试创建日志记录{{1}以编程方式。我试图建立一个处理程序,但是没有办法让它知道在异常时处于活动状态的Handler

我尝试创建自己的类来实现HttpServletRequestServletRequestListener,然后注册一个知道ServletContextListener变量的自定义日志Handler,然后设置并清除ThreadLocal<ServletRequest>中的ThreadLocal变量。在我的ServletRequestListener文件中添加<listener>引用后,正确调用web.xmlcontextInitialized我的日志记录处理程序的requestInitialized方法永远不会被调用发生异常时

此处的代码就在这里。

publish

我正在做的事情有一个小错误吗?这是完全错误的做法吗?是否有一个已经存在的模块可以做我想要的,我还没找到?还有另一种方法可以做我想做的事吗?

This post建议采用类似于我所采用的方法,但没有详细信息。

1 个答案:

答案 0 :(得分: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);
            }
        }
    }

由于这是使用本地线程,如果记录器和过滤器之间存在线程切换,它将无法工作。