View Javadoc

1   package ch.qos.logback.access.jetty;
2   
3   import java.io.File;
4   import java.util.HashMap;
5   import java.util.Iterator;
6   import java.util.List;
7   
8   import org.mortbay.jetty.Request;
9   import org.mortbay.jetty.RequestLog;
10  import org.mortbay.jetty.Response;
11  
12  import ch.qos.logback.access.joran.JoranConfigurator;
13  import ch.qos.logback.access.spi.AccessEvent;
14  import ch.qos.logback.core.Appender;
15  import ch.qos.logback.core.ContextBase;
16  import ch.qos.logback.core.CoreConstants;
17  import ch.qos.logback.core.filter.Filter;
18  import ch.qos.logback.core.joran.spi.JoranException;
19  import ch.qos.logback.core.spi.AppenderAttachable;
20  import ch.qos.logback.core.spi.AppenderAttachableImpl;
21  import ch.qos.logback.core.spi.FilterAttachable;
22  import ch.qos.logback.core.spi.FilterAttachableImpl;
23  import ch.qos.logback.core.spi.FilterReply;
24  import ch.qos.logback.core.status.ErrorStatus;
25  import ch.qos.logback.core.status.InfoStatus;
26  import ch.qos.logback.core.status.WarnStatus;
27  import ch.qos.logback.core.util.OptionHelper;
28  
29  /**
30   * This class is logback's implementation of jetty's RequestLog interface. <p>
31   * It can be seen as logback classic's LoggerContext. Appenders can be attached
32   * directly to RequestLogImpl and RequestLogImpl uses the same StatusManager as
33   * LoggerContext does. It also provides containers for properties. <p> To
34   * configure jetty in order to use RequestLogImpl, the following lines must be
35   * added to the jetty configuration file, namely <em>etc/jetty.xml</em>:
36   * 
37   * <pre>
38   *    &lt;Ref id=&quot;requestLog&quot;&gt; 
39   *      &lt;Set name=&quot;requestLog&quot;&gt; 
40   *        &lt;New id=&quot;requestLogImpl&quot; class=&quot;ch.qos.logback.access.jetty.RequestLogImpl&quot;&gt;&lt;/New&gt;
41   *      &lt;/Set&gt; 
42   *    &lt;/Ref&gt;
43   * </pre>
44   * 
45   * By default, RequestLogImpl looks for a logback configuration file called
46   * logback-access.xml, in the same folder where jetty.xml is located, that is
47   * <em>etc/logback-access.xml</em>. The logback-access.xml file is slightly
48   * different than the usual logback classic configuration file. Most of it is
49   * the same: Appenders and Layouts are declared the exact same way. However,
50   * loggers elements are not allowed. <p> It is possible to put the logback
51   * configuration file anywhere, as long as it's path is specified. Here is
52   * another example, with a path to the logback-access.xml file.
53   * 
54   * <pre>
55   *    &lt;Ref id=&quot;requestLog&quot;&gt; 
56   *      &lt;Set name=&quot;requestLog&quot;&gt; 
57   *        &lt;New id=&quot;requestLogImpl&quot; class=&quot;ch.qos.logback.access.jetty.RequestLogImpl&quot;&gt;&lt;/New&gt;
58   *          &lt;Set name=&quot;fileName&quot;&gt;path/to/logback.xml&lt;/Set&gt;
59   *      &lt;/Set&gt; 
60   *    &lt;/Ref&gt;
61   * </pre>
62   * 
63   * <p> Here is a sample logback-access.xml file that can be used right away:
64   * 
65   * <pre>
66   *    &lt;configuration&gt; 
67   *      &lt;appender name=&quot;STDOUT&quot; class=&quot;ch.qos.logback.core.ConsoleAppender&quot;&gt; 
68   *        &lt;layout class=&quot;ch.qos.logback.access.PatternLayout&quot;&gt; 
69   *          &lt;param name=&quot;Pattern&quot; value=&quot;%date %server %remoteIP %clientHost %user %requestURL&quot; /&gt;
70   *        &lt;/layout&gt; 
71   *      &lt;/appender&gt; 
72   *      
73   *      &lt;appender-ref ref=&quot;STDOUT&quot; /&gt; 
74   *    &lt;/configuration&gt;
75   * </pre>
76   * 
77   * <p> Another configuration file, using SMTPAppender, could be:
78   * 
79   * <pre>
80   *    &lt;configuration&gt; 
81   *      &lt;appender name=&quot;SMTP&quot; class=&quot;ch.qos.logback.access.net.SMTPAppender&quot;&gt;
82   *        &lt;layout class=&quot;ch.qos.logback.access.PatternLayout&quot;&gt;
83   *          &lt;param name=&quot;pattern&quot; value=&quot;%remoteIP [%date] %requestURL %statusCode %bytesSent&quot; /&gt;
84   *        &lt;/layout&gt;
85   *        &lt;param name=&quot;From&quot; value=&quot;sender@domaine.org&quot; /&gt;
86   *        &lt;param name=&quot;SMTPHost&quot; value=&quot;mail.domain.org&quot; /&gt;
87   *         &lt;param name=&quot;Subject&quot; value=&quot;Last Event: %statusCode %requestURL&quot; /&gt;
88   *         &lt;param name=&quot;To&quot; value=&quot;server_admin@domain.org&quot; /&gt;
89   *      &lt;/appender&gt;
90   *      &lt;appender-ref ref=&quot;SMTP&quot; /&gt; 
91   *    &lt;/configuration&gt;
92   * </pre>
93   * 
94   * @author Ceki G&uuml;lc&uuml;
95   * @author S&eacute;bastien Pennec
96   */
97  public class RequestLogImpl extends ContextBase implements RequestLog,
98      AppenderAttachable<AccessEvent>, FilterAttachable<AccessEvent> {
99  
100   public final static String DEFAULT_CONFIG_FILE = "etc" + File.separatorChar
101       + "logback-access.xml";
102 
103   AppenderAttachableImpl<AccessEvent> aai = new AppenderAttachableImpl<AccessEvent>();
104   FilterAttachableImpl<AccessEvent> fai = new FilterAttachableImpl<AccessEvent>();
105   String filename;
106   boolean started = false;
107 
108   public RequestLogImpl() {
109     putObject(CoreConstants.EVALUATOR_MAP, new HashMap());
110   }
111 
112   public void log(Request jettyRequest, Response jettyResponse) {
113     JettyServerAdapter adapter = new JettyServerAdapter(jettyRequest,
114         jettyResponse);
115     AccessEvent accessEvent = new AccessEvent(jettyRequest, jettyResponse,
116         adapter);
117     if (getFilterChainDecision(accessEvent) == FilterReply.DENY) {
118       return;
119     }
120     aai.appendLoopOnAppenders(accessEvent);
121   }
122 
123   public void start() {
124     if (filename == null) {
125       String jettyHomeProperty = OptionHelper.getSystemProperty("jetty.home");
126 
127       filename = jettyHomeProperty + File.separatorChar + DEFAULT_CONFIG_FILE;
128       getStatusManager().add(
129           new WarnStatus("filename property not set. Assuming [" + filename
130               + "]", this));
131 
132     }
133     try {
134       File configFile = new File(filename);
135       if (configFile.exists()) {
136         JoranConfigurator jc = new JoranConfigurator();
137         jc.setContext(this);
138         jc.doConfigure(filename);
139 
140       } else {
141         getStatusManager().add(
142             new ErrorStatus("[" + filename + "] does not exist", this));
143       }
144 
145       if (getName() == null) {
146         setName("LogbackRequestLog");
147       }
148       RequestLogRegistry.register(this);
149       getStatusManager().add(
150           new InfoStatus("RequestLog added to RequestLogRegistry with name: "
151               + getName(), this));
152 
153       started = true;
154 
155     } catch (JoranException e) {
156       // errors have been registered as status messages
157     }
158   }
159 
160   public void stop() {
161     aai.detachAndStopAllAppenders();
162     started = false;
163   }
164 
165   public boolean isRunning() {
166     return started;
167   }
168 
169   public void setFileName(String filename) {
170     this.filename = filename;
171   }
172 
173   public boolean isStarted() {
174     return started;
175   }
176 
177   public boolean isStarting() {
178     return false;
179   }
180 
181   public boolean isStopping() {
182     return false;
183   }
184 
185   public boolean isStopped() {
186     return !started;
187   }
188 
189   public boolean isFailed() {
190     return false;
191   }
192 
193   public void addAppender(Appender<AccessEvent> newAppender) {
194     aai.addAppender(newAppender);
195   }
196 
197   public Iterator<Appender<AccessEvent>> iteratorForAppenders() {
198     return aai.iteratorForAppenders();
199   }
200 
201   public Appender<AccessEvent> getAppender(String name) {
202     return aai.getAppender(name);
203   }
204 
205   public boolean isAttached(Appender appender) {
206     return aai.isAttached(appender);
207   }
208 
209   public void detachAndStopAllAppenders() {
210     aai.detachAndStopAllAppenders();
211 
212   }
213 
214   public boolean detachAppender(Appender<AccessEvent> appender) {
215     return aai.detachAppender(appender);
216   }
217 
218   public boolean detachAppender(String name) {
219     return aai.detachAppender(name);
220   }
221 
222   public void addFilter(Filter<AccessEvent> newFilter) {
223     fai.addFilter(newFilter);
224   }
225 
226   public void clearAllFilters() {
227     fai.clearAllFilters();
228   }
229 
230   public List<Filter<AccessEvent>> getCopyOfAttachedFiltersList() {
231     return fai.getCopyOfAttachedFiltersList();
232   }
233   
234   public FilterReply getFilterChainDecision(AccessEvent event) {
235     return fai.getFilterChainDecision(event);
236   }
237 
238   public Filter getFirstFilter() {
239     return fai.getFirstFilter();
240   }
241 }