001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.server.log;
031:
032: import com.caucho.log.AbstractRolloverLog;
033: import com.caucho.util.Alarm;
034: import com.caucho.util.FreeList;
035: import com.caucho.util.L10N;
036: import com.caucho.util.ThreadPool;
037:
038: import java.io.IOException;
039: import java.util.ArrayList;
040: import java.util.logging.Level;
041: import java.util.logging.Logger;
042:
043: /**
044: * Represents an log of every top-level request to the server.
045: */
046: public class AccessLogWriter extends AbstractRolloverLog implements
047: Runnable {
048: protected static final L10N L = new L10N(AccessLogWriter.class);
049: protected static final Logger log = Logger
050: .getLogger(AccessLogWriter.class.getName());
051:
052: private static final int BUFFER_SIZE = 65536;
053: private static final int BUFFER_GAP = 8 * 1024;
054:
055: private static final FreeList<AccessLogBuffer> _freeBuffers = new FreeList<AccessLogBuffer>(
056: 4);
057:
058: private final AccessLog _log;
059:
060: private final Object _bufferLock = new Object();
061: private AccessLogBuffer _logBuffer;
062: private byte[] _buffer;
063: private int _length;
064:
065: private boolean _hasThread;
066: private boolean _isFlushing;
067:
068: // the write queue
069: private int _maxQueueLength = 32;
070: private final ArrayList<AccessLogBuffer> _writeQueue = new ArrayList<AccessLogBuffer>();
071:
072: AccessLogWriter(AccessLog log) {
073: _log = log;
074:
075: _logBuffer = getLogBuffer();
076: _buffer = _logBuffer.getBuffer();
077: _length = 0;
078: }
079:
080: private AccessLogBuffer getLogBuffer() {
081: AccessLogBuffer buffer = _freeBuffers.allocate();
082:
083: if (buffer == null)
084: buffer = new AccessLogBuffer();
085:
086: return buffer;
087: }
088:
089: Object getBufferLock() {
090: return _bufferLock;
091: }
092:
093: /**
094: * Returns the current buffer for shared-buffer. _bufferLock
095: * must be synchronized.
096: */
097: byte[] getBuffer(int requiredLength) {
098: if (_buffer.length - _length < requiredLength || isRollover()) {
099: flush();
100: }
101:
102: return _buffer;
103: }
104:
105: /**
106: * Returns the current buffer length for shared-buffer. _bufferLock
107: * must be synchronized.
108: */
109: int getLength() {
110: return _length;
111: }
112:
113: /**
114: * Returns the current buffer length for shared-buffer. _bufferLock
115: * must be synchronized.
116: */
117: void setLength(int length) {
118: _length = length;
119: }
120:
121: void writeBuffer(byte[] buffer, int offset, int length) {
122: synchronized (_bufferLock) {
123: if (_buffer.length - _length < length || isRollover()) {
124: flush();
125: }
126:
127: if (_buffer.length < length)
128: length = _buffer.length;
129:
130: System.arraycopy(buffer, offset, _buffer, _length, length);
131: _length += length;
132: }
133: }
134:
135: void writeThrough(byte[] buffer, int offset, int length) {
136: try {
137: write(buffer, offset, length);
138: flush();
139: } catch (IOException e) {
140: log.log(Level.WARNING, e.toString(), e);
141: }
142: }
143:
144: private AccessLogBuffer write(AccessLogBuffer logBuffer) {
145: while (true) {
146: synchronized (_writeQueue) {
147: if (_writeQueue.size() < _maxQueueLength) {
148: _writeQueue.add(logBuffer);
149:
150: if (!_hasThread) {
151: _hasThread = true;
152: ThreadPool.getThreadPool().startPriority(this );
153: }
154:
155: break;
156: } else if (!_isFlushing) {
157: try {
158: _isFlushing = true;
159: // If the queue is full, call the flush code directly
160: // since the thread pool may be out of threads for
161: // a schedule
162: log
163: .fine("AccessLogWriter flushing log directly.");
164:
165: run();
166: } catch (Throwable e) {
167: log.log(Level.WARNING, e.toString(), e);
168: } finally {
169: _isFlushing = false;
170: }
171: }
172: }
173: }
174:
175: AccessLogBuffer buffer = _freeBuffers.allocate();
176:
177: if (buffer == null)
178: buffer = new AccessLogBuffer();
179:
180: return buffer;
181: }
182:
183: // must be synchronized by _bufferLock.
184: protected void flush() {
185: synchronized (_bufferLock) {
186: if (_length > 0) {
187: _logBuffer.setLength(_length);
188: _logBuffer = write(_logBuffer);
189: _buffer = _logBuffer.getBuffer();
190: _length = 0;
191: }
192: }
193:
194: try {
195: super .flush();
196: } catch (IOException e) {
197: log.log(Level.WARNING, e.toString(), e);
198: }
199: }
200:
201: protected void waitForFlush(long timeout) {
202: long expire;
203:
204: if (!Alarm.isTest())
205: expire = Alarm.getCurrentTime() + timeout;
206: else
207: expire = System.currentTimeMillis() + timeout;
208:
209: synchronized (_writeQueue) {
210: while (true) {
211: if (_writeQueue.size() == 0)
212: return;
213:
214: long delta;
215: if (!Alarm.isTest())
216: delta = expire - Alarm.getCurrentTime();
217: else
218: delta = expire - System.currentTimeMillis();
219:
220: if (delta < 0)
221: return;
222:
223: try {
224: _writeQueue.wait(delta);
225: } catch (Exception e) {
226: }
227: }
228: }
229: }
230:
231: /**
232: * Writes the buffer data to the output stream.
233: */
234: private void writeBuffer(AccessLogBuffer buffer) throws IOException {
235: long now = Alarm.getCurrentTime();
236:
237: write(buffer.getBuffer(), 0, buffer.getLength());
238: super .flush();
239:
240: _freeBuffers.free(buffer);
241:
242: rolloverLog(now);
243: }
244:
245: /**
246: * Closes the log, flushing the results.
247: */
248: public void destroy() throws IOException {
249: }
250:
251: public void run() {
252: while (true) {
253: try {
254: AccessLogBuffer buffer = null;
255:
256: synchronized (_writeQueue) {
257: if (_writeQueue.size() > 0) {
258: buffer = _writeQueue.remove(0);
259: _writeQueue.notifyAll();
260: } else {
261: _hasThread = false;
262: return;
263: }
264: }
265:
266: if (buffer != null) {
267: writeBuffer(buffer);
268: }
269: } catch (Throwable e) {
270: log.log(Level.WARNING, e.toString(), e);
271: }
272: }
273: }
274: }
|