View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.server;
20  
21  import java.io.IOException;
22  import java.io.OutputStream;
23  import java.io.PrintStream;
24  import java.util.Locale;
25  
26  import javax.servlet.AsyncEvent;
27  import javax.servlet.AsyncListener;
28  import javax.servlet.DispatcherType;
29  import javax.servlet.ServletContext;
30  import javax.servlet.ServletContextEvent;
31  import javax.servlet.ServletContextListener;
32  import javax.servlet.ServletRequest;
33  import javax.servlet.ServletRequestEvent;
34  import javax.servlet.ServletRequestListener;
35  import javax.servlet.http.HttpServletRequest;
36  import javax.servlet.http.HttpServletResponse;
37  
38  import org.eclipse.jetty.server.handler.ContextHandler;
39  import org.eclipse.jetty.server.handler.ContextHandler.Context;
40  import org.eclipse.jetty.server.handler.ContextHandler.ContextScopeListener;
41  import org.eclipse.jetty.util.DateCache;
42  import org.eclipse.jetty.util.annotation.ManagedAttribute;
43  import org.eclipse.jetty.util.annotation.ManagedObject;
44  import org.eclipse.jetty.util.annotation.Name;
45  import org.eclipse.jetty.util.component.AbstractLifeCycle;
46  import org.eclipse.jetty.util.log.Log;
47  import org.eclipse.jetty.util.log.Logger;
48  
49  
50  /** A Context Listener that produces additional debug.
51   * This listener if added to a ContextHandler, will produce additional debug information to
52   * either/or a specific log stream or the standard debug log.
53   * The events produced by {@link ServletContextListener}, {@link ServletRequestListener}, 
54   * {@link AsyncListener} and {@link ContextScopeListener} are logged.
55   */
56  @ManagedObject("Debug Listener")
57  public class DebugListener extends AbstractLifeCycle implements ServletContextListener
58  {
59      private static final Logger LOG = Log.getLogger(DebugListener.class);
60      private static final DateCache __date=new DateCache("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH);
61      
62      private final String _attr = String.format("__R%s@%x",this.getClass().getSimpleName(),System.identityHashCode(this));
63  
64      private final PrintStream _out;
65      private boolean _renameThread;
66      private boolean _showHeaders;
67      private boolean _dumpContext;
68  
69      public DebugListener()
70      {
71          this(null,false,false,false);
72      }
73      
74      public DebugListener(@Name("renameThread") boolean renameThread, @Name("showHeaders") boolean showHeaders, @Name("dumpContext") boolean dumpContext)
75      {
76          this(null,renameThread,showHeaders,dumpContext);
77      }
78      
79      public DebugListener(@Name("outputStream") OutputStream out, @Name("renameThread") boolean renameThread, @Name("showHeaders") boolean showHeaders, @Name("dumpContext") boolean dumpContext)
80      {
81          _out=out==null?null:new PrintStream(out);
82          _renameThread=renameThread;
83          _showHeaders=showHeaders;
84          _dumpContext=dumpContext;
85      }
86      
87      @ManagedAttribute("Rename thread within context scope")
88      public boolean isRenameThread()
89      {
90          return _renameThread;
91      }
92  
93      public void setRenameThread(boolean renameThread)
94      {
95          _renameThread = renameThread;
96      }
97  
98      @ManagedAttribute("Show request headers")
99      public boolean isShowHeaders()
100     {
101         return _showHeaders;
102     }
103 
104     public void setShowHeaders(boolean showHeaders)
105     {
106         _showHeaders = showHeaders;
107     }
108 
109     @ManagedAttribute("Dump contexts at start")
110     public boolean isDumpContext()
111     {
112         return _dumpContext;
113     }
114 
115     public void setDumpContext(boolean dumpContext)
116     {
117         _dumpContext = dumpContext;
118     }
119 
120     @Override
121     public void contextInitialized(ServletContextEvent sce)
122     {
123         sce.getServletContext().addListener(_servletRequestListener);        
124         ContextHandler handler =  ContextHandler.getContextHandler(sce.getServletContext());
125         handler.addEventListener(_contextScopeListener);
126         String cname=findContextName(sce.getServletContext());
127         log("^  ctx=%s %s",cname,sce.getServletContext());
128         if (_dumpContext)
129         {
130             if (_out==null)
131                 handler.dumpStdErr();
132             else
133             {
134                 try
135                 {
136                     handler.dump(_out);
137                 }
138                 catch(Exception e)
139                 {
140                     LOG.warn(e);
141                 }
142             }
143         }
144     }
145 
146     @Override
147     public void contextDestroyed(ServletContextEvent sce)
148     {
149         String cname=findContextName(sce.getServletContext());
150         log("v  ctx=%s %s",cname,sce.getServletContext());
151     }
152     
153     protected String findContextName(ServletContext context)
154     {
155         if (context==null)
156             return null;
157         String n = (String)context.getAttribute(_attr);
158         if (n==null)
159         {
160             n=String.format("%s@%x",context.getContextPath(),context.hashCode());
161             context.setAttribute(_attr,n);
162         }
163         return n;
164     }
165 
166     protected String findRequestName(ServletRequest request)
167     {
168         if (request==null)
169             return null;
170         HttpServletRequest r = (HttpServletRequest)request;
171         String n = (String)request.getAttribute(_attr);
172         if (n==null)
173         {
174             n=String.format("%s@%x",r.getRequestURI(),request.hashCode());
175             request.setAttribute(_attr,n);
176         }
177         return n;
178     }
179     
180     protected void log(String format, Object... arg)
181     {
182         if (!isRunning())
183             return;
184         
185         String s=String.format(format,arg);
186         
187         long now = System.currentTimeMillis();
188         long ms = now%1000;
189         if (_out!=null)
190             _out.printf("%s.%03d:%s%n",__date.formatNow(now),ms,s);
191         if (LOG.isDebugEnabled())
192             LOG.info(s);
193     }
194     
195     final AsyncListener _asyncListener = new AsyncListener()
196     { 
197         @Override
198         public void onTimeout(AsyncEvent event) throws IOException
199         {
200             String cname=findContextName(((AsyncContextEvent)event).getServletContext());
201             String rname=findRequestName(event.getAsyncContext().getRequest());
202             log("!  ctx=%s r=%s onTimeout %s",cname,rname,((AsyncContextEvent)event).getHttpChannelState());
203         }
204         
205         @Override
206         public void onStartAsync(AsyncEvent event) throws IOException
207         {
208             String cname=findContextName(((AsyncContextEvent)event).getServletContext());
209             String rname=findRequestName(event.getAsyncContext().getRequest());
210             log("!  ctx=%s r=%s onStartAsync %s",cname,rname,((AsyncContextEvent)event).getHttpChannelState());
211         }
212         
213         @Override
214         public void onError(AsyncEvent event) throws IOException
215         {
216             String cname=findContextName(((AsyncContextEvent)event).getServletContext());
217             String rname=findRequestName(event.getAsyncContext().getRequest());
218             log("!! ctx=%s r=%s onError %s %s",cname,rname,event.getThrowable(),((AsyncContextEvent)event).getHttpChannelState());
219         }
220         
221         @Override
222         public void onComplete(AsyncEvent event) throws IOException
223         {
224             AsyncContextEvent ace=(AsyncContextEvent)event;
225             String cname=findContextName(ace.getServletContext());
226             String rname=findRequestName(ace.getAsyncContext().getRequest());
227             
228             Request br=Request.getBaseRequest(ace.getAsyncContext().getRequest());
229             Response response = br.getResponse();
230             String headers=_showHeaders?("\n"+response.getHttpFields().toString()):"";
231             
232             log("!  ctx=%s r=%s onComplete %s %d%s",cname,rname,ace.getHttpChannelState(),response.getStatus(),headers);
233         }
234     };
235     
236     final ServletRequestListener _servletRequestListener = new ServletRequestListener()
237     {
238         @Override
239         public void requestInitialized(ServletRequestEvent sre)
240         {
241             String cname=findContextName(sre.getServletContext());
242             HttpServletRequest r = (HttpServletRequest)sre.getServletRequest();
243            
244             String rname=findRequestName(r);
245             DispatcherType d = r.getDispatcherType();
246             if (d==DispatcherType.REQUEST)
247             {
248                 Request br=Request.getBaseRequest(r);
249 
250                 String headers=_showHeaders?("\n"+br.getMetaData().getFields().toString()):"";
251                 
252                 
253                 StringBuffer url=r.getRequestURL();
254                 if (r.getQueryString()!=null)
255                     url.append('?').append(r.getQueryString());
256                 log(">> %s ctx=%s r=%s %s %s %s %s %s%s",d,
257                         cname,
258                         rname,
259                         d,
260                         r.getMethod(),
261                         url.toString(),
262                         r.getProtocol(),
263                         br.getHttpChannel(),
264                         headers);
265             }
266             else
267                 log(">> %s ctx=%s r=%s",d,cname,rname);
268         }
269         
270         @Override
271         public void requestDestroyed(ServletRequestEvent sre)
272         {
273             String cname=findContextName(sre.getServletContext());
274             HttpServletRequest r = (HttpServletRequest)sre.getServletRequest();
275             String rname=findRequestName(r);
276             DispatcherType d = r.getDispatcherType();
277             if (sre.getServletRequest().isAsyncStarted())
278             {
279                 sre.getServletRequest().getAsyncContext().addListener(_asyncListener);
280                 log("<< %s ctx=%s r=%s async=true",d,cname,rname);
281             }
282             else
283             {
284                 Request br=Request.getBaseRequest(r);
285                 String headers=_showHeaders?("\n"+br.getResponse().getHttpFields().toString()):"";
286                 log("<< %s ctx=%s r=%s async=false %d%s",d,cname,rname,Request.getBaseRequest(r).getResponse().getStatus(),headers);
287             }
288         }
289     };
290     
291     final ContextHandler.ContextScopeListener _contextScopeListener = new ContextHandler.ContextScopeListener()
292     {
293         @Override
294         public void enterScope(Context context, Request request, Object reason)
295         {
296             String cname=findContextName(context);
297             if (request==null)
298                 log(">  ctx=%s %s",cname,reason);
299             else
300             {
301                 String rname=findRequestName(request);
302 
303                 if (_renameThread)
304                 {
305                     Thread thread=Thread.currentThread();
306                     thread.setName(String.format("%s#%s",thread.getName(),rname));
307                 }
308             
309                 log(">  ctx=%s r=%s %s",cname,rname,reason);
310             }
311         }
312         
313 
314         @Override
315         public void exitScope(Context context, Request request)
316         {
317             String cname=findContextName(context);
318             if (request==null)
319                 log("<  ctx=%s",cname);
320             else
321             {
322                 String rname=findRequestName(request);
323 
324                 log("<  ctx=%s r=%s",cname,rname);
325                 if (_renameThread)
326                 {
327                     Thread thread=Thread.currentThread();
328                     if (thread.getName().endsWith(rname))
329                         thread.setName(thread.getName().substring(0,thread.getName().length()-rname.length()-1));
330                 }
331             }
332         }   
333     };
334 }