001: /*
002: * ChainSawHandler.java
003: *
004: * Brazil project web application Framework,
005: * export version: 1.1
006: * Copyright (c) 1999-2000 Sun Microsystems, Inc.
007: *
008: * Sun Public License Notice
009: *
010: * The contents of this file are subject to the Sun Public License Version
011: * 1.0 (the "License"). You may not use this file except in compliance with
012: * the License. A copy of the License is included as the file "license.terms",
013: * and also available at http://www.sun.com/
014: *
015: * The Original Code is from:
016: * Brazil project web application Framework release 1.1.
017: * The Initial Developer of the Original Code is: suhler.
018: * Portions created by suhler are Copyright (C) Sun Microsystems, Inc.
019: * All Rights Reserved.
020: *
021: * Contributor(s): cstevens, suhler.
022: *
023: * Version: 1.15
024: * Created by suhler on 99/04/08
025: * Last modified by suhler on 00/12/11 18:45:10
026: */
027:
028: package sunlabs.brazil.handler;
029:
030: import sunlabs.brazil.util.http.HttpUtil;
031:
032: import java.io.BufferedOutputStream;
033: import java.io.DataOutputStream;
034: import java.io.File;
035: import java.io.FileOutputStream;
036: import java.io.OutputStream;
037: import java.io.IOException;
038: import sunlabs.brazil.server.ChainHandler;
039: import sunlabs.brazil.server.Request;
040: import sunlabs.brazil.server.Server;
041:
042: /**
043: * Variant of the chain handler for doing standard logging.
044: * Don't use on fine furniture.
045: * <p>
046: * Output is a variant of the common logfile format.
047: * The common logfile format is as follows:
048: * <pre>
049: * remotehost rfc931 authuser [date] "request" status bytes
050: * </pre>
051: * <dl>
052: * <dt>remotehost
053: * <dd>Remote hostname (or IP number if DNS hostname is not available, or if DNSLookup is Off.
054: * <dt>rfc931
055: * <dd>The remote logname of the user.
056: * <dt>authuser
057: * <dd>The username as which the user has authenticated himself.
058: * <dt>[date]
059: * <dd>Date and time of the request.
060: * <dt>"request"
061: * <dd>The request line exactly as it came from the client.
062: * <dt>status
063: * <dd>The HTTP status code returned to the client.
064: * <dt>bytes
065: * <dd>The content-length of the document transferred.
066: * <dt> "referrer" (optional)
067: * <dd> the referring url
068: * <dt> "user agent" (optional)
069: * <dd> "The user agent making the request
070: * </dl>
071: * <p>
072: * Additional Configuration options:
073: * <dl class=props>
074: * <dt> logFile <dd> The name of the file to write the logs to.
075: * <dt> flush <dd> The number of requests between flushes to the file
076: * </dl>
077: * If the logFile is removed, the server creates a new one.
078: * Thus logs may be truncated by periodically moving them to
079: * another name (at least on unix).
080: * <p>
081: * See the {@link LogHandler} handler for generating logs whose
082: * contents are configurable.
083: *
084: * @author Stephen Uhler
085: * @version 1.15, 00/12/11
086: */
087:
088: public class ChainSawHandler extends ChainHandler {
089: static public final String LOG = "logFile";
090: static public final String FLUSH = "flush";
091: static public final int BUFSIZE = 4096;
092: DataOutputStream log; // where to log the output to
093: File file;
094: int count = 0; // count flushing interval
095: int flush = 1; // how may requests to flush at
096:
097: public boolean init(Server server, String prefix) {
098: String logFile = server.props.getProperty(prefix + LOG,
099: server.hostName + "-" + server.listen.getLocalPort()
100: + ".log");
101: try {
102: flush = Integer.parseInt(server.props.getProperty(prefix
103: + FLUSH, "1"));
104: } catch (NumberFormatException e) {
105: // ignore
106: }
107:
108: try {
109: file = new File(logFile);
110: log = new DataOutputStream(new BufferedOutputStream(
111: new FileOutputStream(file), BUFSIZE));
112: } catch (IOException e) {
113: server.log(Server.LOG_WARNING, prefix, e.toString());
114: return false;
115: }
116: server.log(Server.LOG_DIAGNOSTIC, prefix, "Log file: "
117: + logFile);
118: return super .init(server, prefix);
119: }
120:
121: /**
122: * Run the chain-handler, counting the # of bytes of output generated
123: * by its chained handlers.
124: */
125:
126: public boolean respond(Request request) throws IOException {
127: String req = "\"" + request.method + " " + request.url + " "
128: + request.protocol + "\"";
129: String now = HttpUtil.formatTime();
130: boolean result = false;
131: IOException oops = null;
132:
133: try {
134: result = super .respond(request);
135: } catch (IOException e) {
136: oops = e;
137: }
138: int code = 200;
139: if (!result) {
140: code = 404;
141: }
142: request.out.flush();
143:
144: String refer;
145: if (request.headers.get("Referer") != null) {
146: refer = "\"" + request.headers.get("Referer") + "\"";
147: } else {
148: refer = "-";
149: }
150:
151: String agent;
152: if (request.headers.get("User-Agent") != null) {
153: agent = "\"" + request.headers.get("User-Agent") + "\"";
154: } else {
155: agent = "-";
156: }
157:
158: count = request.out.bytesWritten;
159: String msg = request.getSocket().getInetAddress()
160: .getHostAddress()
161: + " - - ["
162: + now
163: + "] "
164: + " "
165: + req
166: + " "
167: + code
168: + " "
169: + count + " " + refer + " " + agent + "\n";
170:
171: /*
172: * Write to the log file. If there's a problem, try to open
173: * the file again.
174: */
175:
176: if (!file.exists()) {
177: log.flush();
178: request.log(Server.LOG_WARNING, "Log file went away!");
179: log = new DataOutputStream(new BufferedOutputStream(
180: new FileOutputStream(file), BUFSIZE));
181: count = 0;
182: }
183:
184: log.writeBytes(msg);
185: request.log(Server.LOG_DIAGNOSTIC, msg);
186: if (count++ >= flush) {
187: log.flush();
188: count = 0;
189: }
190: if (oops != null) {
191: throw oops;
192: }
193: return result;
194: }
195: }
|