001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.catalina.valves;
019:
020: import java.io.IOException;
021: import java.io.StringReader;
022: import java.net.InetAddress;
023: import java.net.URLEncoder;
024: import java.text.SimpleDateFormat;
025: import java.util.ArrayList;
026: import java.util.Date;
027: import java.util.List;
028: import java.util.TimeZone;
029:
030: import javax.servlet.http.Cookie;
031: import javax.servlet.http.HttpSession;
032:
033: import org.apache.catalina.Lifecycle;
034: import org.apache.catalina.connector.Request;
035: import org.apache.catalina.connector.Response;
036: import org.apache.catalina.util.ServerInfo;
037: import org.apache.juli.logging.Log;
038: import org.apache.juli.logging.LogFactory;
039:
040: /**
041: * An implementation of the W3c Extended Log File Format. See
042: * http://www.w3.org/TR/WD-logfile.html for more information about the format.
043: *
044: * The following fields are supported:
045: * <ul>
046: * <li><code>c-dns</code>: Client hostname</li>
047: * <li><code>c-ip</code>: Client ip address</li>
048: * <li><code>bytes</code>: bytes served</li>
049: * <li><code>cs-method</code>: request method</li>
050: * <li><code>cs-uri</code>: The full uri requested</li>
051: * <li><code>cs-uri-query</code>: The query string</li>
052: * <li><code>cs-uri-stem</code>: The uri without query string</li>
053: * <li><code>date</code>: The date in yyyy-mm-dd format for GMT</li>
054: * <li><code>s-dns</code>: The server dns entry </li>
055: * <li><code>s-ip</code>: The server ip address</li>
056: * <li><code>cs(XXX)</code>: The value of header XXX from client to server</li>
057: * <li><code>sc(XXX)</code>: The value of header XXX from server to client </li>
058: * <li><code>sc-status</code>: The status code</li>
059: * <li><code>time</code>: Time the request was served</li>
060: * <li><code>time-taken</code>: Time (in seconds) taken to serve the request</li>
061: * <li><code>x-A(XXX)</code>: Pull XXX attribute from the servlet context </li>
062: * <li><code>x-C(XXX)</code>: Pull the first cookie of the name XXX </li>
063: * <li><code>x-R(XXX)</code>: Pull XXX attribute from the servlet request </li>
064: * <li><code>x-S(XXX)</code>: Pull XXX attribute from the session </li>
065: * <li><code>x-P(...)</code>: Call request.getParameter(...)
066: * and URLencode it. Helpful to capture
067: * certain POST parameters.
068: * </li>
069: * <li>For any of the x-H(...) the following method will be called from the
070: * HttpServletRequestObject </li>
071: * <li><code>x-H(authType)</code>: getAuthType </li>
072: * <li><code>x-H(characterEncoding)</code>: getCharacterEncoding </li>
073: * <li><code>x-H(contentLength)</code>: getContentLength </li>
074: * <li><code>x-H(locale)</code>: getLocale</li>
075: * <li><code>x-H(protocol)</code>: getProtocol </li>
076: * <li><code>x-H(remoteUser)</code>: getRemoteUser</li>
077: * <li><code>x-H(requestedSessionId)</code>: getGequestedSessionId</li>
078: * <li><code>x-H(requestedSessionIdFromCookie)</code>:
079: * isRequestedSessionIdFromCookie </li>
080: * <li><code>x-H(requestedSessionIdValid)</code>:
081: * isRequestedSessionIdValid</li>
082: * <li><code>x-H(scheme)</code>: getScheme</li>
083: * <li><code>x-H(secure)</code>: isSecure</li>
084: * </ul>
085: *
086: *
087: *
088: * <p>
089: * Log rotation can be on or off. This is dictated by the rotatable
090: * property.
091: * </p>
092: *
093: * <p>
094: * For UvNIX users, another field called <code>checkExists</code>is also
095: * available. If set to true, the log file's existence will be checked before
096: * each logging. This way an external log rotator can move the file
097: * somewhere and tomcat will start with a new file.
098: * </p>
099: *
100: * <p>
101: * For JMX junkies, a public method called </code>rotate</code> has
102: * been made available to allow you to tell this instance to move
103: * the existing log file to somewhere else start writing a new log file.
104: * </p>
105: *
106: * <p>
107: * Conditional logging is also supported. This can be done with the
108: * <code>condition</code> property.
109: * If the value returned from ServletRequest.getAttribute(condition)
110: * yields a non-null value. The logging will be skipped.
111: * </p>
112: *
113: * <p>
114: * For extended attributes coming from a getAttribute() call,
115: * it is you responsibility to ensure there are no newline or
116: * control characters.
117: * </p>
118: *
119: *
120: * @author Tim Funk
121: * @version $Revision: 522854 $ $Date: 2007-03-27 12:10:45 +0200 (mar., 27 mars 2007) $
122: */
123:
124: public class ExtendedAccessLogValve extends AccessLogValve implements
125: Lifecycle {
126:
127: private static Log log = LogFactory
128: .getLog(ExtendedAccessLogValve.class);
129:
130: // ----------------------------------------------------- Instance Variables
131:
132: /**
133: * The descriptive information about this implementation.
134: */
135: protected static final String extendedAccessLogInfo = "org.apache.catalina.valves.ExtendedAccessLogValve/1.0";
136:
137: // ------------------------------------------------------------- Properties
138:
139: /**
140: * Return descriptive information about this implementation.
141: */
142: public String getInfo() {
143: return (extendedAccessLogInfo);
144: }
145:
146: // --------------------------------------------------------- Public Methods
147:
148: // -------------------------------------------------------- Private Methods
149:
150: /**
151: * Wrap the incoming value into quotes and escape any inner
152: * quotes with double quotes.
153: *
154: * @param value - The value to wrap quotes around
155: * @return '-' if empty of null. Otherwise, toString() will
156: * be called on the object and the value will be wrapped
157: * in quotes and any quotes will be escaped with 2
158: * sets of quotes.
159: */
160: private String wrap(Object value) {
161: String svalue;
162: // Does the value contain a " ? If so must encode it
163: if (value == null || "-".equals(value))
164: return "-";
165:
166: try {
167: svalue = value.toString();
168: if ("".equals(svalue))
169: return "-";
170: } catch (Throwable e) {
171: /* Log error */
172: return "-";
173: }
174:
175: /* Wrap all quotes in double quotes. */
176: StringBuffer buffer = new StringBuffer(svalue.length() + 2);
177: buffer.append('\'');
178: int i = 0;
179: while (i < svalue.length()) {
180: int j = svalue.indexOf('\'', i);
181: if (j == -1) {
182: buffer.append(svalue.substring(i));
183: i = svalue.length();
184: } else {
185: buffer.append(svalue.substring(i, j + 1));
186: buffer.append('"');
187: i = j + 2;
188: }
189: }
190:
191: buffer.append('\'');
192: return buffer.toString();
193: }
194:
195: /**
196: * Open the new log file for the date specified by <code>dateStamp</code>.
197: */
198: protected synchronized void open() {
199: super .open();
200: if (currentLogFile.length() == 0) {
201: writer.println("#Fields: " + pattern);
202: writer.println("#Version: 1.0");
203: writer.println("#Software: " + ServerInfo.getServerInfo());
204: }
205: }
206:
207: // ------------------------------------------------------ Lifecycle Methods
208:
209: protected class DateElement implements AccessLogElement {
210: private Date currentDate = new Date(0);
211:
212: private String currentDateString = null;
213:
214: /**
215: * A date formatter to format a Date into a date in the format
216: * "yyyy-MM-dd".
217: */
218: private SimpleDateFormat dateFormatter = new SimpleDateFormat(
219: "yyyy-MM-dd");
220:
221: public DateElement() {
222: dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT"));
223: }
224:
225: public void addElement(StringBuffer buf, Date date,
226: Request request, Response response, long time) {
227: if (currentDate != date) {
228: synchronized (this ) {
229: if (currentDate != date) {
230: currentDateString = dateFormatter.format(date);
231: currentDate = date;
232: }
233: }
234: }
235: buf.append(currentDateString);
236: }
237: }
238:
239: protected class TimeElement implements AccessLogElement {
240: private Date currentDate = new Date(0);
241:
242: private String currentTimeString = null;
243:
244: /**
245: * A date formatter to format a Date into a time in the format
246: * "kk:mm:ss" (kk is a 24-hour representation of the hour).
247: */
248: private SimpleDateFormat timeFormatter = new SimpleDateFormat(
249: "HH:mm:ss");
250:
251: public TimeElement() {
252: timeFormatter.setTimeZone(TimeZone.getTimeZone("GMT"));
253: }
254:
255: public void addElement(StringBuffer buf, Date date,
256: Request request, Response response, long time) {
257: if (currentDate != date) {
258: synchronized (this ) {
259: if (currentDate != date) {
260: currentTimeString = timeFormatter.format(date);
261: currentDate = date;
262: }
263: }
264: }
265: buf.append(currentTimeString);
266: }
267: }
268:
269: protected class RequestHeaderElement implements AccessLogElement {
270: private String header;
271:
272: public RequestHeaderElement(String header) {
273: this .header = header;
274: }
275:
276: public void addElement(StringBuffer buf, Date date,
277: Request request, Response response, long time) {
278: buf.append(wrap(request.getHeader(header)));
279: }
280: }
281:
282: protected class ResponseHeaderElement implements AccessLogElement {
283: private String header;
284:
285: public ResponseHeaderElement(String header) {
286: this .header = header;
287: }
288:
289: public void addElement(StringBuffer buf, Date date,
290: Request request, Response response, long time) {
291: buf.append(wrap(response.getHeader(header)));
292: }
293: }
294:
295: protected class ServletContextElement implements AccessLogElement {
296: private String attribute;
297:
298: public ServletContextElement(String attribute) {
299: this .attribute = attribute;
300: }
301:
302: public void addElement(StringBuffer buf, Date date,
303: Request request, Response response, long time) {
304: buf.append(wrap(request.getContext().getServletContext()
305: .getAttribute(attribute)));
306: }
307: }
308:
309: protected class CookieElement implements AccessLogElement {
310: private String name;
311:
312: public CookieElement(String name) {
313: this .name = name;
314: }
315:
316: public void addElement(StringBuffer buf, Date date,
317: Request request, Response response, long time) {
318: Cookie[] c = request.getCookies();
319: for (int i = 0; c != null && i < c.length; i++) {
320: if (name.equals(c[i].getName())) {
321: buf.append(wrap(c[i].getValue()));
322: }
323: }
324: }
325: }
326:
327: protected class RequestAttributeElement implements AccessLogElement {
328: private String attribute;
329:
330: public RequestAttributeElement(String attribute) {
331: this .attribute = attribute;
332: }
333:
334: public void addElement(StringBuffer buf, Date date,
335: Request request, Response response, long time) {
336: buf.append(wrap(request.getAttribute(attribute)));
337: }
338: }
339:
340: protected class SessionAttributeElement implements AccessLogElement {
341: private String attribute;
342:
343: public SessionAttributeElement(String attribute) {
344: this .attribute = attribute;
345: }
346:
347: public void addElement(StringBuffer buf, Date date,
348: Request request, Response response, long time) {
349: HttpSession session = null;
350: if (request != null) {
351: session = request.getSession(false);
352: if (session != null)
353: buf.append(wrap(session.getAttribute(attribute)));
354: }
355: }
356: }
357:
358: protected class RequestParameterElement implements AccessLogElement {
359: private String parameter;
360:
361: public RequestParameterElement(String parameter) {
362: this .parameter = parameter;
363: }
364:
365: /**
366: * urlEncode the given string. If null or empty, return null.
367: */
368: private String urlEncode(String value) {
369: if (null == value || value.length() == 0) {
370: return null;
371: }
372: return URLEncoder.encode(value);
373: }
374:
375: public void addElement(StringBuffer buf, Date date,
376: Request request, Response response, long time) {
377: buf
378: .append(wrap(urlEncode(request
379: .getParameter(parameter))));
380: }
381: }
382:
383: protected class PatternTokenizer {
384: private StringReader sr = null;
385: private StringBuffer buf = new StringBuffer();
386: private boolean ended = false;
387: private boolean subToken;
388: private boolean parameter;
389:
390: public PatternTokenizer(String str) {
391: sr = new StringReader(str);
392: }
393:
394: public boolean hasSubToken() {
395: return subToken;
396: }
397:
398: public boolean hasParameter() {
399: return parameter;
400: }
401:
402: public String getToken() throws IOException {
403: if (ended)
404: return null;
405:
406: String result = null;
407: subToken = false;
408: parameter = false;
409:
410: int c = sr.read();
411: while (c != -1) {
412: switch (c) {
413: case ' ':
414: result = buf.toString();
415: buf = new StringBuffer();
416: buf.append((char) c);
417: return result;
418: case '-':
419: result = buf.toString();
420: buf = new StringBuffer();
421: subToken = true;
422: return result;
423: case '(':
424: result = buf.toString();
425: buf = new StringBuffer();
426: parameter = true;
427: return result;
428: case ')':
429: result = buf.toString();
430: buf = new StringBuffer();
431: break;
432: default:
433: buf.append((char) c);
434: }
435: c = sr.read();
436: }
437: ended = true;
438: if (buf.length() != 0) {
439: return buf.toString();
440: } else {
441: return null;
442: }
443: }
444:
445: public String getParameter() throws IOException {
446: String result;
447: if (!parameter) {
448: return null;
449: }
450: parameter = false;
451: int c = sr.read();
452: while (c != -1) {
453: if (c == ')') {
454: result = buf.toString();
455: buf = new StringBuffer();
456: return result;
457: }
458: buf.append((char) c);
459: c = sr.read();
460: }
461: return null;
462: }
463:
464: public String getWhiteSpaces() throws IOException {
465: if (isEnded())
466: return "";
467: StringBuffer whiteSpaces = new StringBuffer();
468: if (buf.length() > 0) {
469: whiteSpaces.append(buf);
470: buf = new StringBuffer();
471: }
472: int c = sr.read();
473: while (Character.isWhitespace((char) c)) {
474: whiteSpaces.append((char) c);
475: c = sr.read();
476: }
477: if (c == -1) {
478: ended = true;
479: } else {
480: buf.append((char) c);
481: }
482: return whiteSpaces.toString();
483: }
484:
485: public boolean isEnded() {
486: return ended;
487: }
488:
489: public String getRemains() throws IOException {
490: StringBuffer remains = new StringBuffer();
491: for (int c = sr.read(); c != -1; c = sr.read()) {
492: remains.append((char) c);
493: }
494: return remains.toString();
495: }
496:
497: }
498:
499: protected AccessLogElement[] createLogElements() {
500: if (log.isDebugEnabled()) {
501: log.debug("decodePattern, pattern =" + pattern);
502: }
503: List<AccessLogElement> list = new ArrayList<AccessLogElement>();
504:
505: PatternTokenizer tokenizer = new PatternTokenizer(pattern);
506: try {
507:
508: // Ignore leading whitespace.
509: tokenizer.getWhiteSpaces();
510:
511: if (tokenizer.isEnded()) {
512: log.info("pattern was just empty or whitespace");
513: return null;
514: }
515:
516: String token = tokenizer.getToken();
517: while (token != null) {
518: if (log.isDebugEnabled()) {
519: log.debug("token = " + token);
520: }
521: AccessLogElement element = getLogElement(token,
522: tokenizer);
523: if (element == null) {
524: break;
525: }
526: list.add(element);
527: String whiteSpaces = tokenizer.getWhiteSpaces();
528: if (whiteSpaces.length() > 0) {
529: list.add(new StringElement(whiteSpaces));
530: }
531: if (tokenizer.isEnded()) {
532: break;
533: }
534: token = tokenizer.getToken();
535: }
536: if (log.isDebugEnabled()) {
537: log.debug("finished decoding with element size of: "
538: + list.size());
539: }
540: return (AccessLogElement[]) list
541: .toArray(new AccessLogElement[0]);
542: } catch (IOException e) {
543: log.error("parse error", e);
544: return null;
545: }
546: }
547:
548: protected AccessLogElement getLogElement(String token,
549: PatternTokenizer tokenizer) throws IOException {
550: if ("date".equals(token)) {
551: return new DateElement();
552: } else if ("time".equals(token)) {
553: if (tokenizer.hasSubToken()) {
554: String nextToken = tokenizer.getToken();
555: if ("taken".equals(nextToken)) {
556: return new ElapsedTimeElement(false);
557: }
558: } else {
559: return new TimeElement();
560: }
561: } else if ("bytes".equals(token)) {
562: return new ByteSentElement(true);
563: } else if ("cached".equals(token)) {
564: /* I don't know how to evaluate this! */
565: return new StringElement("-");
566: } else if ("c".equals(token)) {
567: String nextToken = tokenizer.getToken();
568: if ("ip".equals(nextToken)) {
569: return new RemoteAddrElement();
570: } else if ("dns".equals(nextToken)) {
571: return new HostElement();
572: }
573: } else if ("s".equals(token)) {
574: String nextToken = tokenizer.getToken();
575: if ("ip".equals(nextToken)) {
576: return new LocalAddrElement();
577: } else if ("dns".equals(nextToken)) {
578: return new AccessLogElement() {
579: public void addElement(StringBuffer buf, Date date,
580: Request request, Response response,
581: long time) {
582: String value;
583: try {
584: value = InetAddress.getLocalHost()
585: .getHostName();
586: } catch (Throwable e) {
587: value = "localhost";
588: }
589: buf.append(value);
590: }
591: };
592: }
593: } else if ("cs".equals(token)) {
594: return getClientToServerElement(tokenizer);
595: } else if ("sc".equals(token)) {
596: return getServerToClientElement(tokenizer);
597: } else if ("sr".equals(token) || "rs".equals(token)) {
598: return getProxyElement(tokenizer);
599: } else if ("x".equals(token)) {
600: return getXParameterElement(tokenizer);
601: }
602: log.error("unable to decode with rest of chars starting: "
603: + token);
604: return null;
605: }
606:
607: protected AccessLogElement getClientToServerElement(
608: PatternTokenizer tokenizer) throws IOException {
609: if (tokenizer.hasSubToken()) {
610: String token = tokenizer.getToken();
611: if ("method".equals(token)) {
612: return new MethodElement();
613: } else if ("uri".equals(token)) {
614: if (tokenizer.hasSubToken()) {
615: token = tokenizer.getToken();
616: if ("stem".equals(token)) {
617: return new RequestURIElement();
618: } else if ("query".equals(token)) {
619: return new AccessLogElement() {
620: public void addElement(StringBuffer buf,
621: Date date, Request request,
622: Response response, long time) {
623: String query = request.getQueryString();
624: if (query != null) {
625: buf.append(query);
626: } else {
627: buf.append('-');
628: }
629: }
630: };
631: }
632: } else {
633: return new AccessLogElement() {
634: public void addElement(StringBuffer buf,
635: Date date, Request request,
636: Response response, long time) {
637: String query = request.getQueryString();
638: if (query != null) {
639: buf.append(request.getRequestURI());
640: } else {
641: buf.append(request.getRequestURI());
642: buf.append('?');
643: buf.append(request.getQueryString());
644: }
645: }
646: };
647: }
648: }
649: } else if (tokenizer.hasParameter()) {
650: String parameter = tokenizer.getParameter();
651: if (parameter == null) {
652: log.error("No closing ) found for in decode");
653: return null;
654: }
655: return new RequestHeaderElement(parameter);
656: }
657: log.error("The next characters couldn't be decoded: "
658: + tokenizer.getRemains());
659: return null;
660: }
661:
662: protected AccessLogElement getServerToClientElement(
663: PatternTokenizer tokenizer) throws IOException {
664: if (tokenizer.hasSubToken()) {
665: String token = tokenizer.getToken();
666: if ("status".equals(token)) {
667: return new HttpStatusCodeElement();
668: } else if ("comment".equals(token)) {
669: return new StringElement("?");
670: }
671: } else if (tokenizer.hasParameter()) {
672: String parameter = tokenizer.getParameter();
673: if (parameter == null) {
674: log.error("No closing ) found for in decode");
675: return null;
676: }
677: return new ResponseHeaderElement(parameter);
678: }
679: log.error("The next characters couldn't be decoded: "
680: + tokenizer.getRemains());
681: return null;
682: }
683:
684: protected AccessLogElement getProxyElement(
685: PatternTokenizer tokenizer) throws IOException {
686: String token = null;
687: if (tokenizer.hasSubToken()) {
688: token = tokenizer.getToken();
689: return new StringElement("-");
690: } else if (tokenizer.hasParameter()) {
691: tokenizer.getParameter();
692: return new StringElement("-");
693: }
694: log.error("The next characters couldn't be decoded: " + token);
695: return null;
696: }
697:
698: protected AccessLogElement getXParameterElement(
699: PatternTokenizer tokenizer) throws IOException {
700: if (!tokenizer.hasSubToken()) {
701: log
702: .error("x param in wrong format. Needs to be 'x-#(...)' read the docs!");
703: return null;
704: }
705: String token = tokenizer.getToken();
706: if (!tokenizer.hasParameter()) {
707: log
708: .error("x param in wrong format. Needs to be 'x-#(...)' read the docs!");
709: return null;
710: }
711: String parameter = tokenizer.getParameter();
712: if (parameter == null) {
713: log.error("No closing ) found for in decode");
714: return null;
715: }
716: if ("A".equals(token)) {
717: return new ServletContextElement(parameter);
718: } else if ("C".equals(token)) {
719: return new CookieElement(parameter);
720: } else if ("R".equals(token)) {
721: return new RequestAttributeElement(parameter);
722: } else if ("S".equals(token)) {
723: return new SessionAttributeElement(parameter);
724: } else if ("H".equals(token)) {
725: return getServletRequestElement(parameter);
726: } else if ("P".equals(token)) {
727: return new RequestParameterElement(parameter);
728: }
729: log
730: .error("x param for servlet request, couldn't decode value: "
731: + token);
732: return null;
733: }
734:
735: protected AccessLogElement getServletRequestElement(String parameter) {
736: if ("authType".equals(parameter)) {
737: return new AccessLogElement() {
738: public void addElement(StringBuffer buf, Date date,
739: Request request, Response response, long time) {
740: buf.append(wrap(request.getAuthType()));
741: }
742: };
743: } else if ("remoteUser".equals(parameter)) {
744: return new AccessLogElement() {
745: public void addElement(StringBuffer buf, Date date,
746: Request request, Response response, long time) {
747: buf.append(wrap(request.getRemoteUser()));
748: }
749: };
750: } else if ("requestedSessionId".equals(parameter)) {
751: return new AccessLogElement() {
752: public void addElement(StringBuffer buf, Date date,
753: Request request, Response response, long time) {
754: buf.append(wrap(request.getRequestedSessionId()));
755: }
756: };
757: } else if ("requestedSessionIdFromCookie".equals(parameter)) {
758: return new AccessLogElement() {
759: public void addElement(StringBuffer buf, Date date,
760: Request request, Response response, long time) {
761: buf
762: .append(wrap(""
763: + request
764: .isRequestedSessionIdFromCookie()));
765: }
766: };
767: } else if ("requestedSessionIdValid".equals(parameter)) {
768: return new AccessLogElement() {
769: public void addElement(StringBuffer buf, Date date,
770: Request request, Response response, long time) {
771: buf.append(wrap(""
772: + request.isRequestedSessionIdValid()));
773: }
774: };
775: } else if ("contentLength".equals(parameter)) {
776: return new AccessLogElement() {
777: public void addElement(StringBuffer buf, Date date,
778: Request request, Response response, long time) {
779: buf.append(wrap("" + request.getContentLength()));
780: }
781: };
782: } else if ("characterEncoding".equals(parameter)) {
783: return new AccessLogElement() {
784: public void addElement(StringBuffer buf, Date date,
785: Request request, Response response, long time) {
786: buf.append(wrap(request.getCharacterEncoding()));
787: }
788: };
789: } else if ("locale".equals(parameter)) {
790: return new AccessLogElement() {
791: public void addElement(StringBuffer buf, Date date,
792: Request request, Response response, long time) {
793: buf.append(wrap(request.getLocale()));
794: }
795: };
796: } else if ("protocol".equals(parameter)) {
797: return new AccessLogElement() {
798: public void addElement(StringBuffer buf, Date date,
799: Request request, Response response, long time) {
800: buf.append(wrap(request.getProtocol()));
801: }
802: };
803: } else if ("scheme".equals(parameter)) {
804: return new AccessLogElement() {
805: public void addElement(StringBuffer buf, Date date,
806: Request request, Response response, long time) {
807: buf.append(request.getScheme());
808: }
809: };
810: } else if ("secure".equals(parameter)) {
811: return new AccessLogElement() {
812: public void addElement(StringBuffer buf, Date date,
813: Request request, Response response, long time) {
814: buf.append(wrap("" + request.isSecure()));
815: }
816: };
817: }
818: log
819: .error("x param for servlet request, couldn't decode value: "
820: + parameter);
821: return null;
822: }
823:
824: }
|