1 29 30 package com.caucho.server.log; 31 32 import com.caucho.config.ConfigException; 33 import com.caucho.config.types.Bytes; 34 import com.caucho.config.types.Period; 35 import com.caucho.loader.CloseListener; 36 import com.caucho.loader.Environment; 37 import com.caucho.server.connection.AbstractHttpRequest; 38 import com.caucho.server.connection.AbstractHttpResponse; 39 import com.caucho.server.util.CauchoSystem; 40 import com.caucho.util.*; 41 import com.caucho.vfs.Path; 42 43 import javax.annotation.PostConstruct; 44 import javax.servlet.ServletContext ; 45 import javax.servlet.ServletException ; 46 import javax.servlet.http.Cookie ; 47 import javax.servlet.http.HttpServletRequest ; 48 import javax.servlet.http.HttpServletResponse ; 49 import java.io.IOException ; 50 import java.util.ArrayList ; 51 import java.util.logging.Logger ; 52 53 56 public class AccessLog extends AbstractAccessLog implements AlarmListener 57 { 58 protected static final L10N L = new L10N(AccessLog.class); 59 protected static final Logger log 60 = Logger.getLogger(AccessLog.class.getName()); 61 62 private static final long ROLLOVER_SIZE = 1024L * 1024L * 1024L; 64 private static final long DAY = 24L * 3600L * 1000L; 66 private static final long ROLLOVER_CHECK_TIME = 600L * 1000L; 68 69 public static final int BUFFER_SIZE = 65536; 70 private static final int BUFFER_GAP = 8 * 1024; 71 72 private QDate _calendar = QDate.createLocal(); 73 private String _timeFormat; 74 private int _timeFormatSecondOffset = -1; 75 76 private final AccessLogWriter _logWriter = new AccessLogWriter(this); 77 78 private Object _streamLock = new Object (); 80 81 private String _format; 82 private Segment []_segments; 83 84 private boolean _isAutoFlush; 85 86 private boolean _isSharedBuffer = true; 87 private Object _sharedBufferLock; 88 89 private final CharBuffer _cb = new CharBuffer(); 90 91 private final CharBuffer _timeCharBuffer = new CharBuffer(); 92 private final ByteBuffer _timeBuffer = new ByteBuffer(); 93 private long _lastTime; 94 95 private Alarm _alarm = new Alarm(this); 96 private boolean _isActive; 97 98 public AccessLog() 99 { 100 setRolloverSize(new Bytes(ROLLOVER_SIZE)); 101 } 102 103 106 public void setFormat(String format) 107 { 108 _format = format; 109 } 110 111 114 public void setPath(Path path) 115 { 116 super.setPath(path); 117 118 _logWriter.setPath(path); 119 } 120 121 124 public void setPathFormat(String pathFormat) 125 throws ConfigException 126 { 127 super.setPathFormat(pathFormat); 128 129 _logWriter.setPathFormat(pathFormat); 130 } 131 132 135 public void setArchiveFormat(String format) 136 { 137 _logWriter.setArchiveFormat(format); 138 } 139 140 145 public void setRolloverPeriod(Period period) 146 { 147 _logWriter.setRolloverPeriod(period); 148 } 149 150 155 public void setRolloverSize(Bytes bytes) 156 { 157 _logWriter.setRolloverSize(bytes); 158 } 159 160 165 public void setRolloverCheckTime(long period) 166 { 167 _logWriter.setRolloverCheckPeriod(period); 168 } 169 170 173 public void setAutoFlush(boolean isAutoFlush) 174 { 175 _isAutoFlush = isAutoFlush; 176 } 177 178 181 public void setSharedBuffer(boolean isSharedBuffer) 182 { 183 _isSharedBuffer = isSharedBuffer; 184 } 185 186 189 @PostConstruct 190 public void init() 191 throws ServletException , IOException 192 { 193 _isActive = true; 194 195 Environment.addClassLoaderListener(new CloseListener(this)); 196 197 if (_format == null) 198 _format = "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\""; 199 200 ArrayList <Segment> segments = parseFormat(_format); 201 202 _segments = new Segment[segments.size()]; 203 segments.toArray(_segments); 204 205 if (_timeFormat == null || _timeFormat.equals("")) { 206 _timeFormat = "[%d/%b/%Y:%H:%M:%S %z]"; 207 _timeFormatSecondOffset = 0; 208 } 209 210 _logWriter.init(); 211 _sharedBufferLock = _logWriter.getBufferLock(); 212 213 _alarm.queue(60000); 214 } 215 216 219 private ArrayList <Segment> parseFormat(String format) 220 { 221 ArrayList <Segment> segments = new ArrayList <Segment>(); 222 CharBuffer cb = new CharBuffer(); 223 224 int i = 0; 225 while (i < _format.length()) { 226 char ch = _format.charAt(i++); 227 228 if (ch != '%' || i >= _format.length()) { 229 cb.append((char) ch); 230 continue; 231 } 232 233 String arg = null; 234 ch = _format.charAt(i++); 235 if (ch == '>') 236 ch = _format.charAt(i++); 237 else if (ch == '{') { 238 if (cb.length() > 0) 239 segments.add(new Segment(this, Segment.TEXT, cb.toString())); 240 cb.clear(); 241 while (i < _format.length() && _format.charAt(i++) != '}') 242 cb.append(_format.charAt(i - 1)); 243 arg = cb.toString(); 244 cb.clear(); 245 246 ch = _format.charAt(i++); 247 } 248 249 switch (ch) { 250 case 'b': case 'c': 251 case 'h': case 'i': case 'l': case 'n': 252 case 'r': case 's': 253 case 'T': case 'D': case 'o': 254 case 'u': case 'U': 255 if (cb.length() > 0) 256 segments.add(new Segment(this, Segment.TEXT, cb.toString())); 257 cb.clear(); 258 segments.add(new Segment(this, ch, arg)); 259 break; 260 261 case 't': 262 if (cb.length() > 0) 263 segments.add(new Segment(this, Segment.TEXT, cb.toString())); 264 cb.clear(); 265 if (arg != null) 266 _timeFormat = arg; 267 segments.add(new Segment(this, ch, arg)); 268 break; 269 270 default: 271 cb.append('%'); 272 i--; 273 break; 274 } 275 } 276 277 cb.append(CauchoSystem.getNewlineString()); 278 segments.add(new Segment(this, Segment.TEXT, cb.toString())); 279 280 return segments; 281 } 282 283 286 public void log(HttpServletRequest req, 287 HttpServletResponse res, 288 ServletContext application) 289 throws IOException 290 { 291 AbstractHttpRequest request = (AbstractHttpRequest) req; 292 AbstractHttpResponse response = (AbstractHttpResponse) res; 293 294 if (_isSharedBuffer && ! _isAutoFlush) { 295 synchronized (_sharedBufferLock) { 296 byte []buffer = _logWriter.getBuffer(BUFFER_GAP); 297 int length = _logWriter.getLength(); 298 299 length = log(request, response, 300 buffer, length, buffer.length - length); 301 302 _logWriter.setLength(length); 303 } 304 } 305 else { 306 byte []buffer = request.getLogBuffer(); 307 308 int length = log(request, response, buffer, 0, buffer.length); 309 310 if (_isAutoFlush) 311 _logWriter.writeThrough(buffer, 0, length); 312 else 313 _logWriter.writeBuffer(buffer, 0, length); 314 } 315 } 316 317 328 private int log(AbstractHttpRequest request, 329 AbstractHttpResponse response, 330 byte []buffer, int offset, int length) 331 throws IOException 332 { 333 int len = _segments.length; 334 for (int i = 0; i < len; i++) { 335 Segment segment = _segments[i]; 336 String value = null; 337 CharBuffer cbValue = null; 338 CharSegment csValue = null; 339 340 switch (segment._code) { 341 case Segment.TEXT: 342 int sublen = segment._data.length; 343 byte []data = segment._data; 344 for (int j = 0; j < sublen; j++) 345 buffer[offset++] = data[j]; 346 break; 347 348 case Segment.CHAR: 349 buffer[offset++] = segment._ch; 350 break; 351 352 case 'b': 353 if (response.getStatusCode() == 304) 354 buffer[offset++] = (byte) '-'; 355 else 356 offset = print(buffer, offset, response.getContentLength()); 357 break; 358 359 case 'c': 361 Cookie cookie = request.getCookie(segment._string); 362 if (cookie == null) 363 cookie = response.getCookie(segment._string); 364 if (cookie == null) 365 buffer[offset++] = (byte) '-'; 366 else 367 offset = print(buffer, offset, cookie.getValue()); 368 break; 369 370 case Segment.SET_COOKIE: 372 ArrayList cookies = response.getCookies(); 373 if (cookies == null || cookies.size() == 0) 374 buffer[offset++] = (byte) '-'; 375 else { 376 _cb.clear(); 377 response.fillCookie(_cb, (Cookie ) cookies.get(0), 0, 0, false); 378 379 offset = print(buffer, offset, _cb.getBuffer(), 0, _cb.getLength()); 380 } 381 break; 382 383 case 'h': 384 offset = request.printRemoteAddr(buffer, offset); 385 break; 386 387 case 'i': 389 csValue = request.getHeaderBuffer(segment._string); 390 if (csValue == null) 391 buffer[offset++] = (byte) '-'; 392 else 393 offset = print(buffer, offset, csValue); 394 break; 395 396 case 'l': 397 buffer[offset++] = (byte) '-'; 398 break; 399 400 case 'n': 402 Object oValue = request.getAttribute(segment._string); 403 if (oValue == null) 404 buffer[offset++] = (byte) '-'; 405 else 406 offset = print(buffer, offset, String.valueOf(oValue)); 407 break; 408 409 case 'o': 411 value = response.getHeader(segment._string); 412 if (value == null) 413 buffer[offset++] = (byte) '-'; 414 else 415 offset = print(buffer, offset, value); 416 break; 417 418 case 'r': 419 offset = print(buffer, offset, request.getMethod()); 420 421 buffer[offset++] = (byte) ' '; 422 423 data = request.getUriBuffer(); 424 sublen = request.getUriLength(); 425 System.arraycopy(data, 0, buffer, offset, sublen); 426 offset += sublen; 427 buffer[offset++] = (byte) ' '; 428 429 offset = print(buffer, offset, request.getProtocol()); 430 break; 431 432 case 's': 433 int status = response.getStatusCode(); 434 buffer[offset++] = (byte) ('0' + (status / 100) % 10); 435 buffer[offset++] = (byte) ('0' + (status / 10) % 10); 436 buffer[offset++] = (byte) ('0' + status % 10); 437 break; 438 439 case 't': 440 long date = Alarm.getCurrentTime(); 441 442 if (date / 1000 != _lastTime / 1000) 443 fillTime(date); 444 445 sublen = _timeBuffer.getLength(); 446 data = _timeBuffer.getBuffer(); 447 448 synchronized (_timeBuffer) { 449 System.arraycopy(data, 0, buffer, offset, sublen); 450 } 451 452 offset += sublen; 453 break; 454 455 case 'T': 456 { 457 long startTime = request.getStartTime(); 458 long endTime = Alarm.getExactTime(); 459 460 offset = print(buffer, offset, (int) ((endTime - startTime + 500) / 1000)); 461 break; 462 } 463 464 case 'D': 465 { 466 long startTime = request.getStartTime(); 467 long endTime = Alarm.getExactTime(); 468 469 offset = print(buffer, offset, (int) ((endTime - startTime) * 1000)); 470 break; 471 } 472 473 case 'u': 474 value = request.getRemoteUser(false); 475 if (value == null) 476 buffer[offset++] = (byte) '-'; 477 else { 478 buffer[offset++] = (byte) '"'; 479 offset = print(buffer, offset, value); 480 buffer[offset++] = (byte) '"'; 481 } 482 break; 483 484 case 'U': 485 offset = print(buffer, offset, request.getRequestURI()); 486 break; 487 488 default: 489 throw new IOException (); 490 } 491 } 492 493 return offset; 494 } 495 496 504 private int print(byte []buffer, int offset, CharSegment cb) 505 { 506 char []charBuffer = cb.getBuffer(); 507 int cbOffset = cb.getOffset(); 508 int length = cb.getLength(); 509 510 if (buffer.length - offset - 256 < length) 512 length = buffer.length - offset - 256; 513 514 for (int i = length - 1; i >= 0; i--) 515 buffer[offset + i] = (byte) charBuffer[cbOffset + i]; 516 517 return offset + length; 518 } 519 520 528 private int print(byte []buffer, int offset, String s) 529 { 530 int length = s.length(); 531 532 _cb.ensureCapacity(length); 533 char []cBuf = _cb.getBuffer(); 534 535 s.getChars(0, length, cBuf, 0); 536 537 for (int i = length - 1; i >= 0; i--) 538 buffer[offset + i] = (byte) cBuf[i]; 539 540 return offset + length; 541 } 542 543 551 private int print(byte []buffer, int offset, 552 char []cb, int cbOff, int length) 553 { 554 for (int i = length - 1; i >= 0; i--) 555 buffer[offset + i] = (byte) cb[cbOff + i]; 556 557 return offset + length; 558 } 559 560 568 private int print(byte []buffer, int offset, long v) 569 { 570 if (v == 0) { 571 buffer[offset] = (byte) '0'; 572 return offset + 1; 573 } 574 575 if (v < 0) { 576 buffer[offset++] = (byte) '-'; 577 v = -v; 578 } 579 580 int length = 0; 581 int exp = 10; 582 583 for (; exp <= v && exp > 0; length++) 584 exp = 10 * exp; 585 586 offset += length; 587 for (int i = 0; i <= length; i++) { 588 buffer[offset - i] = (byte) (v % 10 + '0'); 589 v = v / 10; 590 } 591 592 return offset + 1; 593 } 594 595 598 public void flush() 599 { 600 _logWriter.flush(); 601 } 602 603 606 public void handleAlarm(Alarm alarm) 607 { 608 try { 609 flush(); 610 } finally { 611 alarm = _alarm; 612 if (alarm != null) 613 alarm.queue(60000); 614 } 615 } 616 617 620 public void destroy() 621 throws IOException 622 { 623 _isActive = false; 624 625 Alarm alarm = _alarm;; 626 _alarm = null; 627 628 if (alarm != null) 629 alarm.dequeue(); 630 631 _logWriter.close(); 632 } 633 634 639 private void fillTime(long date) 640 throws IOException 641 { 642 if (date / 1000 == _lastTime / 1000) 643 return; 644 645 synchronized (_timeBuffer) { 646 if (_timeFormatSecondOffset >= 0 && date / 60000 == _lastTime / 60000) { 647 byte []bBuf = _timeBuffer.getBuffer(); 648 649 int sec = (int) (date / 1000 % 60); 650 651 bBuf[_timeFormatSecondOffset + 0] = (byte) ('0' + sec / 10); 652 bBuf[_timeFormatSecondOffset + 1] = (byte) ('0' + sec % 10); 653 654 return; 655 } 656 657 _timeCharBuffer.clear(); 658 QDate.formatLocal(_timeCharBuffer, date, _timeFormat); 659 660 if (_timeFormatSecondOffset >= 0) 661 _timeFormatSecondOffset = _timeCharBuffer.lastIndexOf(':') + 1; 662 663 char []cBuf = _timeCharBuffer.getBuffer(); 664 int length = _timeCharBuffer.getLength(); 665 666 _timeBuffer.setLength(length); 667 byte []bBuf = _timeBuffer.getBuffer(); 668 669 for (int i = length - 1; i >= 0; i--) 670 bBuf[i] = (byte) cBuf[i]; 671 } 672 673 _lastTime = date; 674 } 675 676 679 static class Segment { 680 final static int TEXT = 0; 681 final static int CHAR = 1; 682 final static int SET_COOKIE = 2; 683 684 int _code; 685 byte []_data; 686 byte _ch; 687 String _string; 688 AccessLog _log; 689 690 697 Segment(AccessLog log, int code, String string) 698 { 699 _log = log; 700 _code = code; 701 702 _string = string; 703 if (string != null) { 704 if (code == 'o' && string.equalsIgnoreCase("Set-Cookie")) 705 _code = SET_COOKIE; 706 707 _data = _string.getBytes(); 708 if (code == TEXT && _string.length() == 1) { 709 _ch = (byte) _string.charAt(0); 710 _code = CHAR; 711 } 712 } 713 } 714 } 715 } 716 | Popular Tags |