如何停止在控制台上打印异常堆栈跟踪信息?

20
我写了一个servlet来处理在我的web应用程序中发生的异常,并将它们映射在web.xml中。
    <error-page>
      <exception-type>java.lang.Exception</exception-type>
      <location>/exceptionHandler</location>
    </error-page>

以下是我在异常处理servlet的service方法中所做的事情:

@Override
    protected void service(HttpServletRequest req, HttpServletResponse arg1)
            throws ServletException, IOException {
         Object attribute = req.getAttribute("javax.servlet.error.exception");
         if(attribute instanceof  SocketException){
            // don't do anything 
         }else{
          super.service(req, arg1);
         }
    }.
问题:

当用户请求某些内容并关闭浏览器时,上述方法不起作用,并且堆栈跟踪会打印到控制台。

问题是:

SocketException发生时,如何停止将堆栈跟踪打印到JBoss控制台?

这样做的原因:

我想避免在一天结束时看到所有日志中的SocketException,因为我无法对这些信息采取任何措施。


1
如果您知道异常发生的原因,那么在它发生时,您应该根据自己的要求处理该异常,而不是像您建议的那样过滤所有异常的出现,即使它们出现的原因与您预期的不同。 - Taylor Hx
1
@Daemon 没有办法处理那个问题。我已经给出了原因 :). 我想做的就是在服务器日志中避免那个堆栈跟踪。 - Suresh Atta
啊,我明白了。异常被保存为“ServletRequest”的属性,并且直到请求被处理时才被捕获。 - Taylor Hx
你确定使用 'java.lang.Exception' 来指定异常类型是可行的吗?尝试使用 java.lang.Throwable,这应该用于指定通用错误处理程序。<exception-type>java.lang.Throwable</exception-type>由于你想要处理 SocketException,你可能想要使用类似以下的代码:<exception-type>java.net.SocketException</exception-type> - Sudarshan_SMD
你能把堆栈跟踪附加到你的问题上吗?谢谢。 - ericson
显示剩余5条评论
7个回答

7

以下是我目前为止作为解决方法所做的工作。

添加了一个过滤器,并劫持了所有请求和响应。捕获异常并检查类型。

/**
 * Hijacks all the http request and response here.
 * Catch the SocketException and do not print 
 * If other exceptions print to console
 * date : 9-18-2013
 * 
 * @author Suresh Atta
 *
 */
public class ExceptionHandler implements Filter {

    @Override
    public void doFilter(ServletRequest arg0, ServletResponse arg1,
            FilterChain arg2) throws IOException, ServletException {
        try{
        arg2.doFilter(arg0, arg1);
        }catch(SocketException e ){
           // Please don't print this to log.    
        }
    }


}

web.xml中,过滤器映射。
<filter>
        <filter-name>ExceptionHandler</filter-name>
        <filter-class>com.nextenders.server.ExceptionHandler</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>ExceptionHandler</filter-name>
        <dispatcher>  REQUEST   </dispatcher>
        <url-pattern> /*</url-pattern>
    </filter-mapping>  

我不会将这个标记为答案,因为我不确定这是否是标准方法。这只是一个解决办法。

1
我会用不同的方式来处理。在你的代码中没有必要捕获异常。我只会捕获 SocketException 或者至少重新抛出错误捕获的异常,以便其他过滤器可以正确地对这些异常做出反应,而不是将其吞噬并打印到控制台上。 - Matthias
@Matthias 感谢您的指出。已经编辑并更新了代码。这段代码是很久以前发布的,最近我更改了逻辑。捕获套接字异常并在那里不做任何处理。没有办法处理它。我已经给出了原因 :)。我想要做的就是避免服务器日志中的堆栈跟踪。 - Suresh Atta
3
我理解你想要实现的目标,这确实可以避免日志出现误导性的异常。唯一能够“处理SocketException”的方法就是向你的普通日志中写入一条小消息(可能是在debug/info级别),说明用户已终止请求。这样在生产环境中出现问题并需要跟踪日志文件时,可以更容易地跟踪会话。我有一个规则,记录应用程序进出所有东西,这样我就能追踪事物。 - Matthias
@Matthias 是的,是的。现在正在做同样的事情。在那个catch块里面有一些业务逻辑来跟踪事物 :) - Suresh Atta
1
太好了,那么我相信你的解决方法实际上是一个不错的方案。 - Matthias

7

您可以尝试在web.xml中直接添加socket异常,而不是添加java.lang.Exception

<error-page>
  <exception-type>java.net.SocketException</exception-type>
  <location>/exceptionHandler</location>
</error-page>

你可以在servlet中什么也不做,或者添加一个空的jsp文件,如下所示:

<error-page>
  <exception-type>java.net.SocketException</exception-type>
  <location>/error.jsp</location>
</error-page>

3

使用 Jboss 自定义日志处理程序 来解决这个问题。

如果你不想记录任何 SocketException 异常的堆栈跟踪,那么在将其记录到文件时可以跳过它。

需要遵循以下步骤:

  • 自定义处理程序必须继承 java.util.logging.Handler

以下是代码:

package com.custom.jboss.logging;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Date;
import java.util.logging.ErrorManager;
import java.util.logging.Handler;
import java.util.logging.LogRecord;

public class SocketExceptionCustomLoggingHandler extends Handler {

    private String logFile;
    public BufferedWriter out = null;

    public SocketExceptionCustomLoggingHandler() {
        super();
        logFile = "";
    }

    @Override
    public void publish(LogRecord record) {
        if (!initialize()) {
            return;
        }
        if (isLoggable(record)) {
            process(record);
        }
    }

    private synchronized boolean initialize() {
        if (out == null && logFile != null && !logFile.equals("")) {
            FileWriter fstream = null;
            try {
                fstream = new FileWriter(logFile, true);
                out = new BufferedWriter(fstream);
            } catch (IOException e) {
                reportError(e.getMessage(), e, ErrorManager.OPEN_FAILURE);
            }
            logToFile("Log file initialized. Logging to: " + logFile);
        }
        return true;
    }

    private void process(LogRecord logRecord) {

        String log = getFormatter().format(logRecord);
        if (log.indexOf("java.net.SocketException") == -1) {
            logToFile(log);
        }
    }

    private void logToFile(String text) {
        try {
            if (out != null) {
                out.write((new Date()).toString() + "\t" + text + "\n");
                out.flush();
            }
        } catch (IOException e) {
            reportError(e.getMessage(), e, ErrorManager.WRITE_FAILURE);
        }

    }

    @Override
    public void flush() {
        try {
            if (out != null) {
                out.flush();
            }
        } catch (IOException e) {
            reportError(e.getMessage(), e, ErrorManager.FLUSH_FAILURE);
        }
    }

    @Override
    public void close() {
        if (out != null) {
            try {
                out.close();
            } catch (IOException e) {
                reportError(e.getMessage(), e, ErrorManager.CLOSE_FAILURE);
            }

        }
    }

    public void setLogFile(String logFile) {
        this.logFile = logFile;
    }

}
  • The file is then packaged into a jar and placed in the modules directory.

    i.e. Jboss-7.1.1/modules/com/custom/jboss/loggers/main together with a module.xml file. The contents of the module.xml should look like this.

    <?xml version="1.0" encoding="UTF-8"?>   
    <module xmlns="urn:jboss:module:1.0" name="com.custom.jboss.loggers">  
         <resources>      
               <resource-root path="SocketExceptionHandler.jar"/>  
              <!-- Insert resources here -->   
         </resources>    
         <dependencies>      
              <module name="org.jboss.logging"/>  
              <module name="javax.api"/>   
         </dependencies>  
    </module>  
    
  • Then modify the standalone.xml to support logging to the custom logger

    <subsystem xmlns="urn:jboss:domain:logging:1.1">
        ...
        <custom-handler name="SocketExceptionAppender" class="com.custom.jboss.logging.SocketExceptionCustomLoggingHandler" module="com.custom.jboss.loggers">
            <level name="DEBUG"/>
            <formatter>
                <pattern-formatter pattern="%d{HH:mm:ss,SSS} %-5p [%c] (%t) %s%E%n"/>
            </formatter>
            <properties>
                <property name="logFile" value="d:\\temp\\logfile.txt"/>
            </properties>
        </custom-handler>
        ...
        <root-logger>
            <level name="INFO"/>
            <handlers>
                <handler name="SocketExceptionAppender"/>                    
            </handlers>
        </root-logger>
    </subsystem>
    
  • Add more handler in root-logger if needed such as FILE, CONSOLE etc.

  • Now all the logs will be logged in your custom log file via this custom handler where we have skipped SocketException
  • We can make this class more generic passing properties from standalone.xml such as used to pass log file path.

如果有任何问题,请告知我。


感谢您宝贵的回复。我会尽力尝试并回复您。 - Suresh Atta

2
您可能需要重写fillInStackTrace方法。
public static class CustomException extends Exception {
@Override
public Throwable fillInStackTrace() {
    return null;
}       

}


为了编写这个,我需要控制HTTP响应。这正阻止着我去完成它。 - Suresh Atta

2
据我所知,未被容器捕获的异常会记录在控制台上。因此,使用过滤器捕获未处理的异常是有意义的。
除此之外,默认情况下,Struts2也有一个名为"exception interceptor"的拦截器。请参见 defaultStack。如果需要自定义异常处理,则需要覆盖该拦截器。
另外,我会额外记录这些异常的日志(至少记录到一个errors-ignore.txt文件中),而不是完全跳过它们。

1
另一个有趣的想法:您可以创建异常处理程序。
class SocketExceptionSwallower implements Thread.UncaughtExceptionHandler {
        public void uncaughtException(Thread t, Throwable e) {
            if (e instanceof SocketException) {
                // swallow
            } else {
                e.printStackTrace();
            }
        }
    }

and registered with

Thread.setDefaultUncaughtExceptionHandler(new SocketExceptionSwallower());

or

Thread.currentThread().setUncaughtExceptionHandler(new SocketExceptionSwallower());

Simple, no overhead of another filter in chain, but it messes with threads and will probably break something in Java EE environment )
Might be useful in testing/experimenting or if you fully control threads in your own code


1
你可以使用Logger。为此,您需要log4j库,并将所有所需的行为和异常写入日志文件。为此,您需要提供日志文件路径。

你能提供一个例子,说明 log4j 如何防止未处理异常堆栈跟踪的输出吗? - oxfn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接