间歇问题是指有时出现有时不出现,很难找出也很难再生的问题。查找间歇问题与其他调试任务很相似,同样要应用科学方法。不过,关键是要尽可能多地进行观察,并尽可能多地收集数据样本。下面将介绍一些有关的技术,可以帮助增加可能有助于查找问题的关键信息。
除非你已经很清楚当前的间歇问题,否则大多数间歇问题都很难再生,因此通常可以把信息作为注释增加到作为HTTP响应一部分的HTML文档中,这是一种很有用的技术。这些注释可以作为线索帮助我们找出问题。为此,一种方法是创建一个字符串ArrayList,并将其放到请求中。JSP生成页面时,会把串列表作为注释追加到请求HTML文档的最后。用户遇到问题时,可以点击浏览器中的ViewèSource来阅读有关的注释,或者把这些注释剪切粘贴到一个电子邮件消息中。要注意,这些注释块中不要放敏感信息。
还有一个技术是让用户访问一个侦查应用(snoop application)。侦查应用有许多变种; Tomcat甚至还提供了一个非常轻量级的snoop.jsp。其基本思想是,应用把它对当前上下文的了解统统回显给用户,再从浏览器复制这个信息,发回给开发人员来帮助完成调试。
下面的代码展示了一个名为SuperSnoop的servlet,它会打印request、session和application作用域中的所有信息。这个servlet比其他大多数侦查应用都更为全面。有些情况下,可能你希望定制这个应用,确保不会暴露敏感信息。
import java.io.*;
import java.net.*;
import java.text.DateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.Properties;
import javax.servlet.*;
import javax.servlet.http.*;
public class SuperSnoop extends HttpServlet {
private String makeTitle( String title )
{
return new String("<H2>"+title+"</H2>\n");
}
private String makeRow( String left, String right ) {
StringBuffer sb = new StringBuffer(128);
sb.append( "<tr>\n" );
sb.append( "\t<td>"+left+"</td>\n");
sb.append( "\t<td>"+right+"</td>\n");
sb.append( "</tr>\n" );
return sb.toString();
}
private String today()
{
Date now = new Date();
DateFormat dateFormatter = DateFormat.getDateTimeInstance();
return dateFormatter.format( now );
}
private String showHeaders( HttpServletRequest request ) {
StringBuffer sb = new StringBuffer(512);
Enumeration e = request.getHeaderNames();
String key = null;
String value = null;
sb.append( makeTitle("Request Headers"));
sb.append("<table>");
while( e.hasMoreElements() ){
key = (String)e.nextElement();
value = request.getHeader(key);
sb.append( makeRow( key, value ));
}
sb.append("</table>");
return sb.toString();
}
private String showRequestInfo(HttpServletRequest request) {
StringBuffer sb = new StringBuffer(512);
sb.append(makeTitle( "Request Info"));
sb.append("<table>\n");
sb.append( makeRow( "ContextPath", request.getContextPath() ));
sb.append( makeRow( "Method", request.getMethod() ));
sb.append( makeRow( "RequestURL", request.getRequestURL().toString()));
sb.append( makeRow( "ServletPath", request.getServletPath() ));
sb.append( makeRow( "CharacterEncoding", request.getCharacterEncoding() ));
sb.append( makeRow( "ContentType", request.getContentType() ));
sb.append( makeRow( "RemoteAddress", request.getRemoteAddr() ));
sb.append( makeRow( "RemoteHost", request.getRemoteHost() ));
sb.append( makeRow( "Scheme", request.getScheme() ));
sb.append( makeRow( "ServerName", request.getServerName() ));
sb.append( makeRow( "ServerPort", Integer.toString(request.getServerPort())
));
sb.append("</table>\n");
return sb.toString();
}
private String showRequestParams( HttpServletRequest request )
{
StringBuffer sb = new StringBuffer(512);
Enumeration e = request.getParameterNames();
String key = null;
String value = null;
sb.append(makeTitle("Request Parameters"));
sb.append("<table>");
while( e.hasMoreElements() ){
key = (String)e.nextElement();
value = request.getParameter(key);
sb.append( makeRow( key, value ));
}
sb.append("</table>");
return sb.toString();
}
private String showSysProps() {
StringBuffer sb = new StringBuffer(512);
Properties p = System.getProperties();
Enumeration e = p.propertyNames();
String key = null;
String value = null;
sb.append(makeTitle("System Properties"));
sb.append("<table>");
sb.append(makeRow("System Time", today() ));
while( e.hasMoreElements() ){
key = (String)e.nextElement();
value = p.getProperty( key );
sb.append( makeRow( key, value ));
}
sb.append("</table>");
return sb.toString();
}
private String showInitParams() {
StringBuffer sb = new StringBuffer(512);
ServletContext context = getServletContext();
Enumeration e = context.getInitParameterNames();
String key = null;
String value = null;
sb.append(makeTitle("Initialization Parameters"));
sb.append("<table>");
while( e.hasMoreElements() ){
key = (String)e.nextElement();
value = context.getInitParameter(key);
sb.append( makeRow( key, value ));
}
sb.append("</table>");
return sb.toString();
}
private String showServletContextInfo()
{
StringBuffer sb = new StringBuffer(512);
ServletContext context = getServletContext();
sb.append(makeTitle("Servlet Context"));
sb.append("<table>");
sb.append( makeRow( "Server Info", context.getServerInfo()));
sb.append( makeRow( "Servlet Context Name",
context.getServletContextName()));
sb.append( makeRow( "Real Path", context.getRealPath("/")));
sb.append("</table>");
return sb.toString();
}
private String showContextAttributes() {
StringBuffer sb = new StringBuffer(512);
ServletContext context = getServletContext();
Enumeration e = context.getAttributeNames();
String key = null;
String value = null;
sb.append(makeTitle("Servlet Context Attributes"));
sb.append("<table>");
while( e.hasMoreElements() ){
key = (String)e.nextElement();
value = context.getAttribute(key).toString();
sb.append( makeRow( key, value ));
}
sb.append("</table>");
return sb.toString();
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<head>");
out.println("<title>SysProps Servlet</title>");
out.println("</head>");
out.println("<body>");
out.println( today() );
out.println( showHeaders(request) );
out.println( showRequestInfo(request) );
out.println( showRequestParams(request) );
out.println( showServletContextInfo() );
out.println( showContextAttributes() );
out.println( showInitParams() );
out.println( showSysProps() );
out.println("</table>");
out.println("</body>");
out.println("</html>");
out.close();
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
doGet(request, response);
}
public String getServletInfo() {
return "Prints Java System Properties";
}
}
这个servlet代码相当简单,不需要做多少解释。如果用户执行这个servlet遇到问题,结果会剪切粘贴到一个电子邮件消息中。其中包括会话ID信息,所以,如果有NdcFilter ,就能很容易地找出有关这个用户会话的日志信息。
一旦从用户收集到关键的信息,日志系统就能如前所述动态地重新配置,捕获到所需的详细信息来帮助服务器端用户再生bug。







