View Javadoc

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