001: //========================================================================
002: //Copyright 1997-2006 Mort Bay Consulting Pty. Ltd.
003: //------------------------------------------------------------------------
004: //Licensed under the Apache License, Version 2.0 (the "License");
005: //you may not use this file except in compliance with the License.
006: //You may obtain a copy of the License at
007: //http://www.apache.org/licenses/LICENSE-2.0
008: //Unless required by applicable law or agreed to in writing, software
009: //distributed under the License is distributed on an "AS IS" BASIS,
010: //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
011: //See the License for the specific language governing permissions and
012: //limitations under the License.
013: //========================================================================
014:
015: package org.mortbay.jetty;
016:
017: import java.io.IOException;
018: import java.io.OutputStream;
019: import java.io.OutputStreamWriter;
020: import java.io.Writer;
021: import java.util.Locale;
022: import java.util.TimeZone;
023:
024: import javax.servlet.http.Cookie;
025:
026: import org.mortbay.component.AbstractLifeCycle;
027: import org.mortbay.jetty.servlet.PathMap;
028: import org.mortbay.log.Log;
029: import org.mortbay.util.DateCache;
030: import org.mortbay.util.RolloverFileOutputStream;
031: import org.mortbay.util.StringUtil;
032: import org.mortbay.util.TypeUtil;
033:
034: /**
035: * This {@link RequestLog} implementation outputs logs in the pseudo-standard NCSA common log format.
036: * Configuration options allow a choice between the standard Common Log Format (as used in the 3 log format)
037: * and the Combined Log Format (single log format).
038: * This log format can be output by most web servers, and almost all web log analysing software can understand
039: * these formats.
040: * @author Greg Wilkins
041: * @author Nigel Canonizado
042: *
043: * @org.apache.xbean.XBean element="ncsaLog"
044: */
045: public class NCSARequestLog extends AbstractLifeCycle implements
046: RequestLog {
047: private String _filename;
048: private boolean _extended;
049: private boolean _append;
050: private int _retainDays;
051: private boolean _closeOut;
052: private boolean _preferProxiedForAddress;
053: private String _logDateFormat = "dd/MM/yyyy:HH:mm:ss ZZZ";
054: private Locale _logLocale = Locale.getDefault();
055: private String _logTimeZone = TimeZone.getDefault().getID();
056: private String[] _ignorePaths;
057: private boolean _logLatency = false;
058: private boolean _logCookies = false;
059: private boolean _logServer = false;
060:
061: private transient OutputStream _out;
062: private transient OutputStream _fileOut;
063: private transient DateCache _logDateCache;
064: private transient PathMap _ignorePathMap;
065: private transient Writer _writer;
066:
067: public NCSARequestLog() {
068: _extended = true;
069: _append = true;
070: _retainDays = 31;
071: }
072:
073: public NCSARequestLog(String filename) {
074: _extended = true;
075: _append = true;
076: _retainDays = 31;
077: setFilename(filename);
078: }
079:
080: public void setFilename(String filename) {
081: if (filename != null) {
082: filename = filename.trim();
083: if (filename.length() == 0)
084: filename = null;
085: }
086: _filename = filename;
087: }
088:
089: public String getFilename() {
090: return _filename;
091: }
092:
093: public String getDatedFilename() {
094: if (_fileOut instanceof RolloverFileOutputStream)
095: return ((RolloverFileOutputStream) _fileOut)
096: .getDatedFilename();
097: return null;
098: }
099:
100: public void setLogDateFormat(String format) {
101: _logDateFormat = format;
102: }
103:
104: public String getLogDateFormat() {
105: return _logDateFormat;
106: }
107:
108: public void setLogTimeZone(String tz) {
109: _logTimeZone = tz;
110: }
111:
112: public String getLogTimeZone() {
113: return _logTimeZone;
114: }
115:
116: public void setRetainDays(int retainDays) {
117: _retainDays = retainDays;
118: }
119:
120: public int getRetainDays() {
121: return _retainDays;
122: }
123:
124: public void setExtended(boolean extended) {
125: _extended = extended;
126: }
127:
128: public boolean isExtended() {
129: return _extended;
130: }
131:
132: public void setAppend(boolean append) {
133: _append = append;
134: }
135:
136: public boolean isAppend() {
137: return _append;
138: }
139:
140: public void setIgnorePaths(String[] ignorePaths) {
141: _ignorePaths = ignorePaths;
142: }
143:
144: public String[] getIgnorePaths() {
145: return _ignorePaths;
146: }
147:
148: public void setLogCookies(boolean logCookies) {
149: _logCookies = logCookies;
150: }
151:
152: public boolean getLogCookies() {
153: return _logCookies;
154: }
155:
156: public boolean getLogServer() {
157: return _logServer;
158: }
159:
160: public void setLogServer(boolean logServer) {
161: _logServer = logServer;
162: }
163:
164: public void setLogLatency(boolean logLatency) {
165: _logLatency = logLatency;
166: }
167:
168: public boolean getLogLatency() {
169: return _logLatency;
170: }
171:
172: public void setPreferProxiedForAddress(
173: boolean preferProxiedForAddress) {
174: _preferProxiedForAddress = preferProxiedForAddress;
175: }
176:
177: public void log(Request request, Response response) {
178: if (!isStarted())
179: return;
180:
181: try {
182: if (_ignorePathMap != null
183: && _ignorePathMap.getMatch(request.getRequestURI()) != null)
184: return;
185:
186: if (_fileOut == null)
187: return;
188:
189: StringBuffer buf = new StringBuffer(160);
190:
191: if (_logServer) {
192: buf.append(request.getServerName());
193: buf.append(' ');
194: }
195:
196: String addr = null;
197: if (_preferProxiedForAddress) {
198: addr = request.getHeader(HttpHeaders.X_FORWARDED_FOR);
199: }
200:
201: if (addr == null)
202: addr = request.getRemoteAddr();
203:
204: buf.append(addr);
205: buf.append(" - ");
206: String user = request.getRemoteUser();
207: buf.append((user == null) ? " - " : user);
208: buf.append(" [");
209: buf.append(_logDateCache.format(request.getTimeStamp()));
210: buf.append("] \"");
211: buf.append(request.getMethod());
212: buf.append(' ');
213: buf.append(request.getUri());
214: buf.append(' ');
215: buf.append(request.getProtocol());
216: buf.append("\" ");
217: int status = response.getStatus();
218: if (status <= 0)
219: status = 404;
220: buf.append((char) ('0' + ((status / 100) % 10)));
221: buf.append((char) ('0' + ((status / 10) % 10)));
222: buf.append((char) ('0' + (status % 10)));
223:
224: long responseLength = response.getContentCount();
225: if (responseLength >= 0) {
226: buf.append(' ');
227: if (responseLength > 99999)
228: buf.append(Long.toString(responseLength));
229: else {
230: if (responseLength > 9999)
231: buf
232: .append((char) ('0' + ((responseLength / 10000) % 10)));
233: if (responseLength > 999)
234: buf
235: .append((char) ('0' + ((responseLength / 1000) % 10)));
236: if (responseLength > 99)
237: buf
238: .append((char) ('0' + ((responseLength / 100) % 10)));
239: if (responseLength > 9)
240: buf
241: .append((char) ('0' + ((responseLength / 10) % 10)));
242: buf.append((char) ('0' + (responseLength) % 10));
243: }
244: buf.append(' ');
245: } else
246: buf.append(" - ");
247:
248: String log = buf.toString();
249: synchronized (_writer) {
250: _writer.write(log);
251: if (_extended) {
252: logExtended(request, response, _writer);
253: if (!_logCookies)
254: _writer.write(" -");
255: }
256:
257: if (_logCookies) {
258: Cookie[] cookies = request.getCookies();
259: if (cookies == null || cookies.length == 0)
260: _writer.write(" -");
261: else {
262: _writer.write(" \"");
263: for (int i = 0; i < cookies.length; i++) {
264: if (i != 0)
265: _writer.write(';');
266: _writer.write(cookies[i].getName());
267: _writer.write('=');
268: _writer.write(cookies[i].getValue());
269: }
270: _writer.write("\"");
271: }
272: }
273:
274: if (_logLatency) {
275: _writer.write(" ");
276: _writer.write(TypeUtil.toString(System
277: .currentTimeMillis()
278: - request.getTimeStamp()));
279: }
280:
281: _writer.write(StringUtil.__LINE_SEPARATOR);
282: _writer.flush();
283:
284: }
285: } catch (IOException e) {
286: Log.warn(e);
287: }
288:
289: }
290:
291: protected void logExtended(Request request, Response response,
292: Writer writer) throws IOException {
293: String referer = request.getHeader(HttpHeaders.REFERER);
294: if (referer == null)
295: writer.write("\"-\" ");
296: else {
297: writer.write('"');
298: writer.write(referer);
299: writer.write("\" ");
300: }
301:
302: String agent = request.getHeader(HttpHeaders.USER_AGENT);
303: if (agent == null)
304: writer.write("\"-\" ");
305: else {
306: writer.write('"');
307: writer.write(agent);
308: writer.write('"');
309: }
310:
311: }
312:
313: protected void doStart() throws Exception {
314: _logDateCache = new DateCache(_logDateFormat, _logLocale);
315: _logDateCache.setTimeZoneID(_logTimeZone);
316:
317: if (_filename != null) {
318: //TODO the _fileOut should not be null
319: /**
320: * The previous implementation uses RollOverFileOutputStream
321: */
322: _fileOut = new RolloverFileOutputStream(_filename, _append,
323: _retainDays);
324: _closeOut = true;
325: } else
326: _fileOut = System.err;
327:
328: _out = _fileOut;
329:
330: if (_ignorePaths != null && _ignorePaths.length > 0) {
331: _ignorePathMap = new PathMap();
332: for (int i = 0; i < _ignorePaths.length; i++)
333: _ignorePathMap.put(_ignorePaths[i], _ignorePaths[i]);
334: } else
335: _ignorePathMap = null;
336:
337: _writer = new OutputStreamWriter(_out);
338: super .doStart();
339: }
340:
341: protected void doStop() throws Exception {
342: super .doStop();
343: try {
344: if (_writer != null)
345: _writer.flush();
346: } catch (IOException e) {
347: Log.ignore(e);
348: }
349: if (_out != null && _closeOut)
350: try {
351: _out.close();
352: } catch (IOException e) {
353: Log.ignore(e);
354: }
355:
356: _out = null;
357: _fileOut = null;
358: _closeOut = false;
359: _logDateCache = null;
360: _writer = null;
361: }
362:
363: }
|