0001: /*
0002: * Copyright 1999-2001,2004 The Apache Software Foundation.
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016:
0017: package org.apache.catalina.valves;
0018:
0019: import java.io.File;
0020: import java.io.FileWriter;
0021: import java.io.IOException;
0022: import java.io.PrintWriter;
0023: import java.net.InetAddress;
0024: import java.text.DecimalFormat;
0025: import java.text.SimpleDateFormat;
0026: import java.util.Date;
0027: import java.util.TimeZone;
0028:
0029: import javax.servlet.ServletException;
0030: import javax.servlet.ServletRequest;
0031: import javax.servlet.ServletResponse;
0032: import javax.servlet.http.Cookie;
0033: import javax.servlet.http.HttpServletRequest;
0034: import javax.servlet.http.HttpServletResponse;
0035: import javax.servlet.http.HttpSession;
0036:
0037: import org.apache.catalina.HttpResponse;
0038: import org.apache.catalina.Lifecycle;
0039: import org.apache.catalina.LifecycleException;
0040: import org.apache.catalina.LifecycleListener;
0041: import org.apache.catalina.Request;
0042: import org.apache.catalina.Response;
0043: import org.apache.catalina.ValveContext;
0044: import org.apache.catalina.util.LifecycleSupport;
0045: import org.apache.catalina.util.StringManager;
0046:
0047: /**
0048: * <p>Implementation of the <b>Valve</b> interface that generates a web server
0049: * access log with the detailed line contents matching a configurable pattern.
0050: * The syntax of the available patterns is similar to that supported by the
0051: * Apache <code>mod_log_config</code> module. As an additional feature,
0052: * automatic rollover of log files when the date changes is also supported.</p>
0053: *
0054: * <p>Patterns for the logged message may include constant text or any of the
0055: * following replacement strings, for which the corresponding information
0056: * from the specified Response is substituted:</p>
0057: * <ul>
0058: * <li><b>%a</b> - Remote IP address
0059: * <li><b>%A</b> - Local IP address
0060: * <li><b>%b</b> - Bytes sent, excluding HTTP headers, or '-' if no bytes
0061: * were sent
0062: * <li><b>%B</b> - Bytes sent, excluding HTTP headers
0063: * <li><b>%h</b> - Remote host name
0064: * <li><b>%H</b> - Request protocol
0065: * <li><b>%l</b> - Remote logical username from identd (always returns '-')
0066: * <li><b>%m</b> - Request method
0067: * <li><b>%p</b> - Local port
0068: * <li><b>%q</b> - Query string (prepended with a '?' if it exists, otherwise
0069: * an empty string
0070: * <li><b>%r</b> - First line of the request
0071: * <li><b>%s</b> - HTTP status code of the response
0072: * <li><b>%S</b> - User session ID
0073: * <li><b>%t</b> - Date and time, in Common Log Format format
0074: * <li><b>%u</b> - Remote user that was authenticated
0075: * <li><b>%U</b> - Requested URL path
0076: * <li><b>%v</b> - Local server name
0077: * <li><b>%D</b> - Time taken to process the request, in millis
0078: * <li><b>%T</b> - Time taken to process the request, in seconds
0079: * </ul>
0080: * <p>In addition, the caller can specify one of the following aliases for
0081: * commonly utilized patterns:</p>
0082: * <ul>
0083: * <li><b>common</b> - <code>%h %l %u %t "%r" %s %b</code>
0084: * <li><b>combined</b> -
0085: * <code>%h %l %u %t "%r" %s %b "%{Referer}i" "%{User-Agent}i"</code>
0086: * </ul>
0087: *
0088: * <p>
0089: * There is also support to write information from the cookie, incoming
0090: * header, the Session or something else in the ServletRequest.<br>
0091: * It is modeled after the apache syntax:
0092: * <ul>
0093: * <li><code>%{xxx}i</code> for incoming headers
0094: * <li><code>%{xxx}c</code> for a specific cookie
0095: * <li><code>%{xxx}r</code> xxx is an attribute in the ServletRequest
0096: * <li><code>%{xxx}s</code> xxx is an attribute in the HttpSession
0097: * </ul>
0098: * </p>
0099: *
0100: * <p>
0101: * Conditional logging is also supported. This can be done with the
0102: * <code>condition</code> property.
0103: * If the value returned from ServletRequest.getAttribute(condition)
0104: * yields a non-null value. The logging will be skipped.
0105: * </p>
0106: *
0107: * @author Craig R. McClanahan
0108: * @author Jason Brittain
0109: * @version $Revision: 1.10 $ $Date: 2004/02/27 14:58:52 $
0110: */
0111:
0112: public final class AccessLogValve extends ValveBase implements
0113: Lifecycle {
0114:
0115: // ----------------------------------------------------------- Constructors
0116:
0117: /**
0118: * Construct a new instance of this class with default property values.
0119: */
0120: public AccessLogValve() {
0121:
0122: super ();
0123: setPattern("common");
0124:
0125: }
0126:
0127: // ----------------------------------------------------- Instance Variables
0128:
0129: /**
0130: * The as-of date for the currently open log file, or a zero-length
0131: * string if there is no open log file.
0132: */
0133: private String dateStamp = "";
0134:
0135: /**
0136: * The directory in which log files are created.
0137: */
0138: private String directory = "logs";
0139:
0140: /**
0141: * The descriptive information about this implementation.
0142: */
0143: protected static final String info = "org.apache.catalina.valves.AccessLogValve/1.0";
0144:
0145: /**
0146: * The lifecycle event support for this component.
0147: */
0148: protected LifecycleSupport lifecycle = new LifecycleSupport(this );
0149:
0150: /**
0151: * The set of month abbreviations for log messages.
0152: */
0153: protected static final String months[] = { "Jan", "Feb", "Mar",
0154: "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov",
0155: "Dec" };
0156:
0157: /**
0158: * If the current log pattern is the same as the common access log
0159: * format pattern, then we'll set this variable to true and log in
0160: * a more optimal and hard-coded way.
0161: */
0162: private boolean common = false;
0163:
0164: /**
0165: * For the combined format (common, plus useragent and referer), we do
0166: * the same
0167: */
0168: private boolean combined = false;
0169:
0170: /**
0171: * The pattern used to format our access log lines.
0172: */
0173: private String pattern = null;
0174:
0175: /**
0176: * The prefix that is added to log file filenames.
0177: */
0178: private String prefix = "access_log.";
0179:
0180: /**
0181: * Should we rotate our log file? Default is true (like old behavior)
0182: */
0183: private boolean rotatable = true;
0184:
0185: /**
0186: * The string manager for this package.
0187: */
0188: private StringManager sm = StringManager
0189: .getManager(Constants.Package);
0190:
0191: /**
0192: * Has this component been started yet?
0193: */
0194: private boolean started = false;
0195:
0196: /**
0197: * The suffix that is added to log file filenames.
0198: */
0199: private String suffix = "";
0200:
0201: /**
0202: * The PrintWriter to which we are currently logging, if any.
0203: */
0204: private PrintWriter writer = null;
0205:
0206: /**
0207: * A date formatter to format a Date into a date in the format
0208: * "yyyy-MM-dd".
0209: */
0210: private SimpleDateFormat dateFormatter = null;
0211:
0212: /**
0213: * A date formatter to format Dates into a day string in the format
0214: * "dd".
0215: */
0216: private SimpleDateFormat dayFormatter = null;
0217:
0218: /**
0219: * A date formatter to format a Date into a month string in the format
0220: * "MM".
0221: */
0222: private SimpleDateFormat monthFormatter = null;
0223:
0224: /**
0225: * Time taken formatter for 3 decimal places.
0226: */
0227: private DecimalFormat timeTakenFormatter = null;
0228:
0229: /**
0230: * A date formatter to format a Date into a year string in the format
0231: * "yyyy".
0232: */
0233: private SimpleDateFormat yearFormatter = null;
0234:
0235: /**
0236: * A date formatter to format a Date into a time in the format
0237: * "kk:mm:ss" (kk is a 24-hour representation of the hour).
0238: */
0239: private SimpleDateFormat timeFormatter = null;
0240:
0241: /**
0242: * The time zone relative to GMT.
0243: */
0244: private String timeZone = null;
0245:
0246: /**
0247: * The system time when we last updated the Date that this valve
0248: * uses for log lines.
0249: */
0250: private Date currentDate = null;
0251:
0252: /**
0253: * When formatting log lines, we often use strings like this one (" ").
0254: */
0255: private String space = " ";
0256:
0257: /**
0258: * Resolve hosts.
0259: */
0260: private boolean resolveHosts = false;
0261:
0262: /**
0263: * Instant when the log daily rotation was last checked.
0264: */
0265: private long rotationLastChecked = 0L;
0266:
0267: /**
0268: * Are we doing conditional logging. default false.
0269: */
0270: private String condition = null;
0271:
0272: /**
0273: * Date format to place in log file name. Use at your own risk!
0274: */
0275: private String fileDateFormat = null;
0276:
0277: // ------------------------------------------------------------- Properties
0278:
0279: /**
0280: * Return the directory in which we create log files.
0281: */
0282: public String getDirectory() {
0283:
0284: return (directory);
0285:
0286: }
0287:
0288: /**
0289: * Set the directory in which we create log files.
0290: *
0291: * @param directory The new log file directory
0292: */
0293: public void setDirectory(String directory) {
0294:
0295: this .directory = directory;
0296:
0297: }
0298:
0299: /**
0300: * Return descriptive information about this implementation.
0301: */
0302: public String getInfo() {
0303:
0304: return (info);
0305:
0306: }
0307:
0308: /**
0309: * Return the format pattern.
0310: */
0311: public String getPattern() {
0312:
0313: return (this .pattern);
0314:
0315: }
0316:
0317: /**
0318: * Set the format pattern, first translating any recognized alias.
0319: *
0320: * @param pattern The new pattern
0321: */
0322: public void setPattern(String pattern) {
0323:
0324: if (pattern == null)
0325: pattern = "";
0326: if (pattern.equals(Constants.AccessLog.COMMON_ALIAS))
0327: pattern = Constants.AccessLog.COMMON_PATTERN;
0328: if (pattern.equals(Constants.AccessLog.COMBINED_ALIAS))
0329: pattern = Constants.AccessLog.COMBINED_PATTERN;
0330: this .pattern = pattern;
0331:
0332: if (this .pattern.equals(Constants.AccessLog.COMMON_PATTERN))
0333: common = true;
0334: else
0335: common = false;
0336:
0337: if (this .pattern.equals(Constants.AccessLog.COMBINED_PATTERN))
0338: combined = true;
0339: else
0340: combined = false;
0341:
0342: }
0343:
0344: /**
0345: * Return the log file prefix.
0346: */
0347: public String getPrefix() {
0348:
0349: return (prefix);
0350:
0351: }
0352:
0353: /**
0354: * Set the log file prefix.
0355: *
0356: * @param prefix The new log file prefix
0357: */
0358: public void setPrefix(String prefix) {
0359:
0360: this .prefix = prefix;
0361:
0362: }
0363:
0364: /**
0365: * Should we rotate the logs
0366: */
0367: public boolean isRotatable() {
0368:
0369: return rotatable;
0370:
0371: }
0372:
0373: /**
0374: * Set the value is we should we rotate the logs
0375: *
0376: * @param rotatable true is we should rotate.
0377: */
0378: public void setRotatable(boolean rotatable) {
0379:
0380: this .rotatable = rotatable;
0381:
0382: }
0383:
0384: /**
0385: * Return the log file suffix.
0386: */
0387: public String getSuffix() {
0388:
0389: return (suffix);
0390:
0391: }
0392:
0393: /**
0394: * Set the log file suffix.
0395: *
0396: * @param suffix The new log file suffix
0397: */
0398: public void setSuffix(String suffix) {
0399:
0400: this .suffix = suffix;
0401:
0402: }
0403:
0404: /**
0405: * Set the resolve hosts flag.
0406: *
0407: * @param resolveHosts The new resolve hosts value
0408: */
0409: public void setResolveHosts(boolean resolveHosts) {
0410:
0411: this .resolveHosts = resolveHosts;
0412:
0413: }
0414:
0415: /**
0416: * Get the value of the resolve hosts flag.
0417: */
0418: public boolean isResolveHosts() {
0419:
0420: return resolveHosts;
0421:
0422: }
0423:
0424: /**
0425: * Return whether the attribute name to look for when
0426: * performing conditional loggging. If null, every
0427: * request is logged.
0428: */
0429: public String getCondition() {
0430:
0431: return condition;
0432:
0433: }
0434:
0435: /**
0436: * Set the ServletRequest.attribute to look for to perform
0437: * conditional logging. Set to null to log everything.
0438: *
0439: * @param condition Set to null to log everything
0440: */
0441: public void setCondition(String condition) {
0442:
0443: this .condition = condition;
0444:
0445: }
0446:
0447: /**
0448: * Return the date format date based log rotation.
0449: */
0450: public String getFileDateFormat() {
0451: return fileDateFormat;
0452: }
0453:
0454: /**
0455: * Set the date format date based log rotation.
0456: */
0457: public void setFileDateFormat(String fileDateFormat) {
0458: this .fileDateFormat = fileDateFormat;
0459: }
0460:
0461: // --------------------------------------------------------- Public Methods
0462:
0463: /**
0464: * Log a message summarizing the specified request and response, according
0465: * to the format specified by the <code>pattern</code> property.
0466: *
0467: * @param request Request being processed
0468: * @param response Response being processed
0469: * @param context The valve context used to invoke the next valve
0470: * in the current processing pipeline
0471: *
0472: * @exception IOException if an input/output error has occurred
0473: * @exception ServletException if a servlet error has occurred
0474: */
0475: public void invoke(Request request, Response response,
0476: ValveContext context) throws IOException, ServletException {
0477:
0478: // Pass this request on to the next valve in our pipeline
0479: long t1 = System.currentTimeMillis();
0480:
0481: context.invokeNext(request, response);
0482:
0483: long t2 = System.currentTimeMillis();
0484: long time = t2 - t1;
0485:
0486: if (condition != null
0487: && null != request.getRequest().getAttribute(condition)) {
0488: return;
0489: }
0490:
0491: Date date = getDate();
0492: StringBuffer result = new StringBuffer();
0493:
0494: // Check to see if we should log using the "common" access log pattern
0495: if (common || combined) {
0496: String value = null;
0497:
0498: ServletRequest req = request.getRequest();
0499: HttpServletRequest hreq = (HttpServletRequest) req;
0500:
0501: if (isResolveHosts())
0502: result.append(req.getRemoteHost());
0503: else
0504: result.append(req.getRemoteAddr());
0505:
0506: result.append(" - ");
0507:
0508: value = hreq.getRemoteUser();
0509: if (value == null)
0510: result.append("- ");
0511: else {
0512: result.append(value);
0513: result.append(space);
0514: }
0515:
0516: result.append("[");
0517: result.append(dayFormatter.format(date)); // Day
0518: result.append('/');
0519: result.append(lookup(monthFormatter.format(date))); // Month
0520: result.append('/');
0521: result.append(yearFormatter.format(date)); // Year
0522: result.append(':');
0523: result.append(timeFormatter.format(date)); // Time
0524: result.append(space);
0525: result.append(timeZone); // Time Zone
0526: result.append("] \"");
0527:
0528: result.append(hreq.getMethod());
0529: result.append(space);
0530: result.append(hreq.getRequestURI());
0531: if (hreq.getQueryString() != null) {
0532: result.append('?');
0533: result.append(hreq.getQueryString());
0534: }
0535: result.append(space);
0536: result.append(hreq.getProtocol());
0537: result.append("\" ");
0538:
0539: result.append(((HttpResponse) response).getStatus());
0540:
0541: result.append(space);
0542:
0543: int length = response.getContentCount();
0544:
0545: if (length <= 0)
0546: value = "-";
0547: else
0548: value = "" + length;
0549: result.append(value);
0550:
0551: if (combined) {
0552: result.append(space);
0553: result.append("\"");
0554: String referer = hreq.getHeader("referer");
0555: if (referer != null)
0556: result.append(referer);
0557: else
0558: result.append("-");
0559: result.append("\"");
0560:
0561: result.append(space);
0562: result.append("\"");
0563: String ua = hreq.getHeader("user-agent");
0564: if (ua != null)
0565: result.append(ua);
0566: else
0567: result.append("-");
0568: result.append("\"");
0569: }
0570:
0571: } else {
0572: // Generate a message based on the defined pattern
0573: boolean replace = false;
0574: for (int i = 0; i < pattern.length(); i++) {
0575: char ch = pattern.charAt(i);
0576: if (replace) {
0577: /* For code that processes {, the behavior will be ... if I
0578: * do not enounter a closing } - then I ignore the {
0579: */
0580: if ('{' == ch) {
0581: StringBuffer name = new StringBuffer();
0582: int j = i + 1;
0583: for (; j < pattern.length()
0584: && '}' != pattern.charAt(j); j++) {
0585: name.append(pattern.charAt(j));
0586: }
0587: if (j + 1 < pattern.length()) {
0588: /* the +1 was to account for } which we increment now */
0589: j++;
0590: result.append(replace(name.toString(),
0591: pattern.charAt(j), request,
0592: response));
0593: i = j; /*Since we walked more than one character*/
0594: } else {
0595: //D'oh - end of string - pretend we never did this
0596: //and do processing the "old way"
0597: result.append(replace(ch, date, request,
0598: response, time));
0599: }
0600: } else {
0601: result.append(replace(ch, date, request,
0602: response, time));
0603: }
0604: replace = false;
0605: } else if (ch == '%') {
0606: replace = true;
0607: } else {
0608: result.append(ch);
0609: }
0610: }
0611: }
0612: log(result.toString(), date);
0613:
0614: }
0615:
0616: // -------------------------------------------------------- Private Methods
0617:
0618: /**
0619: * Close the currently open log file (if any)
0620: */
0621: private synchronized void close() {
0622:
0623: if (writer == null)
0624: return;
0625: writer.flush();
0626: writer.close();
0627: writer = null;
0628: dateStamp = "";
0629:
0630: }
0631:
0632: /**
0633: * Log the specified message to the log file, switching files if the date
0634: * has changed since the previous log call.
0635: *
0636: * @param message Message to be logged
0637: * @param date the current Date object (so this method doesn't need to
0638: * create a new one)
0639: */
0640: public void log(String message, Date date) {
0641:
0642: if (rotatable) {
0643: // Only do a logfile switch check once a second, max.
0644: long systime = System.currentTimeMillis();
0645: if ((systime - rotationLastChecked) > 1000) {
0646:
0647: // We need a new currentDate
0648: currentDate = new Date(systime);
0649: rotationLastChecked = systime;
0650:
0651: // Check for a change of date
0652: String tsDate = dateFormatter.format(currentDate);
0653:
0654: // If the date has changed, switch log files
0655: if (!dateStamp.equals(tsDate)) {
0656: synchronized (this ) {
0657: if (!dateStamp.equals(tsDate)) {
0658: close();
0659: dateStamp = tsDate;
0660: open();
0661: }
0662: }
0663: }
0664:
0665: }
0666: }
0667:
0668: // Log this message
0669: if (writer != null) {
0670: writer.println(message);
0671: }
0672:
0673: }
0674:
0675: /**
0676: * Return the month abbreviation for the specified month, which must
0677: * be a two-digit String.
0678: *
0679: * @param month Month number ("01" .. "12").
0680: */
0681: private String lookup(String month) {
0682:
0683: int index;
0684: try {
0685: index = Integer.parseInt(month) - 1;
0686: } catch (Throwable t) {
0687: index = 0; // Can not happen, in theory
0688: }
0689: return (months[index]);
0690:
0691: }
0692:
0693: /**
0694: * Open the new log file for the date specified by <code>dateStamp</code>.
0695: */
0696: private synchronized void open() {
0697:
0698: // Create the directory if necessary
0699: File dir = new File(directory);
0700: if (!dir.isAbsolute())
0701: dir = new File(System.getProperty("catalina.base"),
0702: directory);
0703: dir.mkdirs();
0704:
0705: // Open the current log file
0706: try {
0707: String pathname;
0708: // If no rotate - no need for dateStamp in fileName
0709: if (rotatable) {
0710: pathname = dir.getAbsolutePath() + File.separator
0711: + prefix + dateStamp + suffix;
0712: } else {
0713: pathname = dir.getAbsolutePath() + File.separator
0714: + prefix + suffix;
0715: }
0716: writer = new PrintWriter(new FileWriter(pathname, true),
0717: true);
0718: } catch (IOException e) {
0719: writer = null;
0720: }
0721:
0722: }
0723:
0724: /**
0725: * Return the replacement text for the specified pattern character.
0726: *
0727: * @param pattern Pattern character identifying the desired text
0728: * @param date the current Date so that this method doesn't need to
0729: * create one
0730: * @param request Request being processed
0731: * @param response Response being processed
0732: */
0733: private String replace(char pattern, Date date, Request request,
0734: Response response, long time) {
0735:
0736: String value = null;
0737:
0738: ServletRequest req = request.getRequest();
0739: HttpServletRequest hreq = (HttpServletRequest) req;
0740: ServletResponse res = response.getResponse();
0741: HttpServletResponse hres = (HttpServletResponse) res;
0742:
0743: if (pattern == 'a') {
0744: value = req.getRemoteAddr();
0745: } else if (pattern == 'A') {
0746: try {
0747: value = InetAddress.getLocalHost().getHostAddress();
0748: } catch (Throwable e) {
0749: value = "127.0.0.1";
0750: }
0751: } else if (pattern == 'b') {
0752: int length = response.getContentCount();
0753: if (length <= 0)
0754: value = "-";
0755: else
0756: value = "" + length;
0757: } else if (pattern == 'B') {
0758: value = "" + response.getContentLength();
0759: } else if (pattern == 'h') {
0760: value = req.getRemoteHost();
0761: } else if (pattern == 'H') {
0762: value = req.getProtocol();
0763: } else if (pattern == 'l') {
0764: value = "-";
0765: } else if (pattern == 'm') {
0766: if (hreq != null)
0767: value = hreq.getMethod();
0768: else
0769: value = "";
0770: } else if (pattern == 'p') {
0771: value = "" + req.getServerPort();
0772: } else if (pattern == 'D') {
0773: value = "" + time;
0774: } else if (pattern == 'q') {
0775: String query = null;
0776: if (hreq != null)
0777: query = hreq.getQueryString();
0778: if (query != null)
0779: value = "?" + query;
0780: else
0781: value = "";
0782: } else if (pattern == 'r') {
0783: StringBuffer sb = new StringBuffer();
0784: if (hreq != null) {
0785: sb.append(hreq.getMethod());
0786: sb.append(space);
0787: sb.append(hreq.getRequestURI());
0788: if (hreq.getQueryString() != null) {
0789: sb.append('?');
0790: sb.append(hreq.getQueryString());
0791: }
0792: sb.append(space);
0793: sb.append(hreq.getProtocol());
0794: } else {
0795: sb.append("- - ");
0796: sb.append(req.getProtocol());
0797: }
0798: value = sb.toString();
0799: } else if (pattern == 'S') {
0800: if (hreq != null)
0801: if (hreq.getSession(false) != null)
0802: value = hreq.getSession(false).getId();
0803: else
0804: value = "-";
0805: else
0806: value = "-";
0807: } else if (pattern == 's') {
0808: if (hres != null)
0809: value = "" + ((HttpResponse) response).getStatus();
0810: else
0811: value = "-";
0812: } else if (pattern == 't') {
0813: StringBuffer temp = new StringBuffer("[");
0814: temp.append(dayFormatter.format(date)); // Day
0815: temp.append('/');
0816: temp.append(lookup(monthFormatter.format(date))); // Month
0817: temp.append('/');
0818: temp.append(yearFormatter.format(date)); // Year
0819: temp.append(':');
0820: temp.append(timeFormatter.format(date)); // Time
0821: temp.append(' ');
0822: temp.append(timeZone); // Timezone
0823: temp.append(']');
0824: value = temp.toString();
0825: } else if (pattern == 'T') {
0826: value = timeTakenFormatter.format(time / 1000d);
0827: } else if (pattern == 'u') {
0828: if (hreq != null)
0829: value = hreq.getRemoteUser();
0830: if (value == null)
0831: value = "-";
0832: } else if (pattern == 'U') {
0833: if (hreq != null)
0834: value = hreq.getRequestURI();
0835: else
0836: value = "-";
0837: } else if (pattern == 'v') {
0838: value = req.getServerName();
0839: } else {
0840: value = "???" + pattern + "???";
0841: }
0842:
0843: if (value == null)
0844: return ("");
0845: else
0846: return (value);
0847:
0848: }
0849:
0850: /**
0851: * Return the replacement text for the specified "header/parameter".
0852: *
0853: * @param header The header/parameter to get
0854: * @param type Where to get it from i=input,c=cookie,r=ServletRequest,s=Session
0855: * @param request Request being processed
0856: * @param response Response being processed
0857: */
0858: private String replace(String header, char type, Request request,
0859: Response response) {
0860:
0861: Object value = null;
0862:
0863: ServletRequest req = request.getRequest();
0864: HttpServletRequest hreq = (HttpServletRequest) req;
0865:
0866: switch (type) {
0867: case 'i':
0868: if (null != hreq)
0869: value = hreq.getHeader(header);
0870: else
0871: value = "??";
0872: break;
0873: /*
0874: // Someone please make me work
0875: case 'o':
0876: break;
0877: */
0878: case 'c':
0879: Cookie[] c = hreq.getCookies();
0880: for (int i = 0; c != null && i < c.length; i++) {
0881: if (header.equals(c[i].getName())) {
0882: value = c[i].getValue();
0883: break;
0884: }
0885: }
0886: break;
0887: case 'r':
0888: if (null != hreq)
0889: value = hreq.getAttribute(header);
0890: else
0891: value = "??";
0892: break;
0893: case 's':
0894: if (null != hreq) {
0895: HttpSession sess = hreq.getSession(false);
0896: if (null != sess)
0897: value = sess.getAttribute(header);
0898: }
0899: break;
0900: default:
0901: value = "???";
0902: }
0903:
0904: /* try catch in case toString() barfs */
0905: try {
0906: if (value != null)
0907: if (value instanceof String)
0908: return (String) value;
0909: else
0910: return value.toString();
0911: else
0912: return "-";
0913: } catch (Throwable e) {
0914: return "-";
0915: }
0916: }
0917:
0918: /**
0919: * This method returns a Date object that is accurate to within one
0920: * second. If a thread calls this method to get a Date and it's been
0921: * less than 1 second since a new Date was created, this method
0922: * simply gives out the same Date again so that the system doesn't
0923: * spend time creating Date objects unnecessarily.
0924: *
0925: * @return Date
0926: */
0927: private Date getDate() {
0928: if (currentDate == null) {
0929: currentDate = new Date();
0930: } else {
0931: // Only create a new Date once per second, max.
0932: long systime = System.currentTimeMillis();
0933: if ((systime - currentDate.getTime()) > 1000) {
0934: currentDate = new Date(systime);
0935: }
0936: }
0937:
0938: return currentDate;
0939: }
0940:
0941: private String calculateTimeZoneOffset(long offset) {
0942: StringBuffer tz = new StringBuffer();
0943: if ((offset < 0)) {
0944: tz.append("-");
0945: offset = -offset;
0946: } else {
0947: tz.append("+");
0948: }
0949:
0950: long hourOffset = offset / (1000 * 60 * 60);
0951: long minuteOffset = (offset / (1000 * 60)) % 60;
0952:
0953: if (hourOffset < 10)
0954: tz.append("0");
0955: tz.append(hourOffset);
0956:
0957: if (minuteOffset < 10)
0958: tz.append("0");
0959: tz.append(minuteOffset);
0960:
0961: return tz.toString();
0962: }
0963:
0964: // ------------------------------------------------------ Lifecycle Methods
0965:
0966: /**
0967: * Add a lifecycle event listener to this component.
0968: *
0969: * @param listener The listener to add
0970: */
0971: public void addLifecycleListener(LifecycleListener listener) {
0972:
0973: lifecycle.addLifecycleListener(listener);
0974:
0975: }
0976:
0977: /**
0978: * Get the lifecycle listeners associated with this lifecycle. If this
0979: * Lifecycle has no listeners registered, a zero-length array is returned.
0980: */
0981: public LifecycleListener[] findLifecycleListeners() {
0982:
0983: return lifecycle.findLifecycleListeners();
0984:
0985: }
0986:
0987: /**
0988: * Remove a lifecycle event listener from this component.
0989: *
0990: * @param listener The listener to add
0991: */
0992: public void removeLifecycleListener(LifecycleListener listener) {
0993:
0994: lifecycle.removeLifecycleListener(listener);
0995:
0996: }
0997:
0998: /**
0999: * Prepare for the beginning of active use of the public methods of this
1000: * component. This method should be called after <code>configure()</code>,
1001: * and before any of the public methods of the component are utilized.
1002: *
1003: * @exception LifecycleException if this component detects a fatal error
1004: * that prevents this component from being used
1005: */
1006: public void start() throws LifecycleException {
1007:
1008: // Validate and update our current component state
1009: if (started)
1010: throw new LifecycleException(sm
1011: .getString("accessLogValve.alreadyStarted"));
1012: lifecycle.fireLifecycleEvent(START_EVENT, null);
1013: started = true;
1014:
1015: // Initialize the timeZone, Date formatters, and currentDate
1016: TimeZone tz = TimeZone.getDefault();
1017: timeZone = calculateTimeZoneOffset(tz.getRawOffset());
1018:
1019: if (fileDateFormat == null || fileDateFormat.length() == 0)
1020: fileDateFormat = "yyyy-MM-dd";
1021: dateFormatter = new SimpleDateFormat(fileDateFormat);
1022: dateFormatter.setTimeZone(tz);
1023: dayFormatter = new SimpleDateFormat("dd");
1024: dayFormatter.setTimeZone(tz);
1025: monthFormatter = new SimpleDateFormat("MM");
1026: monthFormatter.setTimeZone(tz);
1027: yearFormatter = new SimpleDateFormat("yyyy");
1028: yearFormatter.setTimeZone(tz);
1029: timeFormatter = new SimpleDateFormat("HH:mm:ss");
1030: timeFormatter.setTimeZone(tz);
1031: currentDate = new Date();
1032: dateStamp = dateFormatter.format(currentDate);
1033: timeTakenFormatter = new DecimalFormat("0.000");
1034:
1035: open();
1036:
1037: }
1038:
1039: /**
1040: * Gracefully terminate the active use of the public methods of this
1041: * component. This method should be the last one called on a given
1042: * instance of this component.
1043: *
1044: * @exception LifecycleException if this component detects a fatal error
1045: * that needs to be reported
1046: */
1047: public void stop() throws LifecycleException {
1048:
1049: // Validate and update our current component state
1050: if (!started)
1051: throw new LifecycleException(sm
1052: .getString("accessLogValve.notStarted"));
1053: lifecycle.fireLifecycleEvent(STOP_EVENT, null);
1054: started = false;
1055:
1056: close();
1057:
1058: }
1059: }
|