View Javadoc

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