PageRenderTime 27ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/projects/tomcat-7.0.2/java/org/apache/catalina/valves/AccessLogValve.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 1427 lines | 779 code | 209 blank | 439 comment | 137 complexity | 2a9ee71322427eb875006d4485df2b48 MD5 | raw file
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. package org.apache.catalina.valves;
  18. import java.io.BufferedWriter;
  19. import java.io.File;
  20. import java.io.FileWriter;
  21. import java.io.IOException;
  22. import java.io.PrintWriter;
  23. import java.net.InetAddress;
  24. import java.text.SimpleDateFormat;
  25. import java.util.ArrayList;
  26. import java.util.Date;
  27. import java.util.Iterator;
  28. import java.util.List;
  29. import java.util.TimeZone;
  30. import javax.servlet.ServletException;
  31. import javax.servlet.http.Cookie;
  32. import javax.servlet.http.HttpSession;
  33. import org.apache.catalina.AccessLog;
  34. import org.apache.catalina.LifecycleException;
  35. import org.apache.catalina.LifecycleState;
  36. import org.apache.catalina.connector.Request;
  37. import org.apache.catalina.connector.Response;
  38. import org.apache.catalina.util.LifecycleBase;
  39. import org.apache.tomcat.util.res.StringManager;
  40. import org.apache.coyote.RequestInfo;
  41. import org.apache.juli.logging.Log;
  42. import org.apache.juli.logging.LogFactory;
  43. /**
  44. * <p>Implementation of the <b>Valve</b> interface that generates a web server
  45. * access log with the detailed line contents matching a configurable pattern.
  46. * The syntax of the available patterns is similar to that supported by the
  47. * Apache <code>mod_log_config</code> module. As an additional feature,
  48. * automatic rollover of log files when the date changes is also supported.</p>
  49. *
  50. * <p>Patterns for the logged message may include constant text or any of the
  51. * following replacement strings, for which the corresponding information
  52. * from the specified Response is substituted:</p>
  53. * <ul>
  54. * <li><b>%a</b> - Remote IP address
  55. * <li><b>%A</b> - Local IP address
  56. * <li><b>%b</b> - Bytes sent, excluding HTTP headers, or '-' if no bytes
  57. * were sent
  58. * <li><b>%B</b> - Bytes sent, excluding HTTP headers
  59. * <li><b>%h</b> - Remote host name
  60. * <li><b>%H</b> - Request protocol
  61. * <li><b>%l</b> - Remote logical username from identd (always returns '-')
  62. * <li><b>%m</b> - Request method
  63. * <li><b>%p</b> - Local port
  64. * <li><b>%q</b> - Query string (prepended with a '?' if it exists, otherwise
  65. * an empty string
  66. * <li><b>%r</b> - First line of the request
  67. * <li><b>%s</b> - HTTP status code of the response
  68. * <li><b>%S</b> - User session ID
  69. * <li><b>%t</b> - Date and time, in Common Log Format format
  70. * <li><b>%u</b> - Remote user that was authenticated
  71. * <li><b>%U</b> - Requested URL path
  72. * <li><b>%v</b> - Local server name
  73. * <li><b>%D</b> - Time taken to process the request, in millis
  74. * <li><b>%T</b> - Time taken to process the request, in seconds
  75. * <li><b>%I</b> - current Request thread name (can compare later with stacktraces)
  76. * </ul>
  77. * <p>In addition, the caller can specify one of the following aliases for
  78. * commonly utilized patterns:</p>
  79. * <ul>
  80. * <li><b>common</b> - <code>%h %l %u %t "%r" %s %b</code>
  81. * <li><b>combined</b> -
  82. * <code>%h %l %u %t "%r" %s %b "%{Referer}i" "%{User-Agent}i"</code>
  83. * </ul>
  84. *
  85. * <p>
  86. * There is also support to write information from the cookie, incoming
  87. * header, the Session or something else in the ServletRequest.<br>
  88. * It is modeled after the apache syntax:
  89. * <ul>
  90. * <li><code>%{xxx}i</code> for incoming headers
  91. * <li><code>%{xxx}o</code> for outgoing response headers
  92. * <li><code>%{xxx}c</code> for a specific cookie
  93. * <li><code>%{xxx}r</code> xxx is an attribute in the ServletRequest
  94. * <li><code>%{xxx}s</code> xxx is an attribute in the HttpSession
  95. * </ul>
  96. * </p>
  97. *
  98. * <p>
  99. * Conditional logging is also supported. This can be done with the
  100. * <code>condition</code> property.
  101. * If the value returned from ServletRequest.getAttribute(condition)
  102. * yields a non-null value. The logging will be skipped.
  103. * </p>
  104. *
  105. * @author Craig R. McClanahan
  106. * @author Jason Brittain
  107. * @author Remy Maucherat
  108. * @author Takayuki Kaneko
  109. * @author Peter Rossbach
  110. *
  111. * @version $Id: AccessLogValve.java 950587 2010-06-02 15:12:36Z markt $
  112. */
  113. public class AccessLogValve extends ValveBase implements AccessLog {
  114. private static final Log log = LogFactory.getLog(AccessLogValve.class);
  115. //------------------------------------------------------ Constructor
  116. public AccessLogValve() {
  117. super(true);
  118. }
  119. // ----------------------------------------------------- Instance Variables
  120. /**
  121. * The as-of date for the currently open log file, or a zero-length
  122. * string if there is no open log file.
  123. */
  124. private volatile String dateStamp = "";
  125. /**
  126. * The directory in which log files are created.
  127. */
  128. private String directory = "logs";
  129. /**
  130. * The descriptive information about this implementation.
  131. */
  132. protected static final String info =
  133. "org.apache.catalina.valves.AccessLogValve/2.1";
  134. /**
  135. * The set of month abbreviations for log messages.
  136. */
  137. protected static final String months[] =
  138. { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  139. "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
  140. /**
  141. * enabled this component
  142. */
  143. protected boolean enabled = true;
  144. /**
  145. * The pattern used to format our access log lines.
  146. */
  147. protected String pattern = null;
  148. /**
  149. * The prefix that is added to log file filenames.
  150. */
  151. protected String prefix = "access_log.";
  152. /**
  153. * Should we rotate our log file? Default is true (like old behavior)
  154. */
  155. protected boolean rotatable = true;
  156. /**
  157. * Buffered logging.
  158. */
  159. private boolean buffered = true;
  160. /**
  161. * The string manager for this package.
  162. */
  163. protected static final StringManager sm =
  164. StringManager.getManager(Constants.Package);
  165. /**
  166. * The suffix that is added to log file filenames.
  167. */
  168. protected String suffix = "";
  169. /**
  170. * The PrintWriter to which we are currently logging, if any.
  171. */
  172. protected PrintWriter writer = null;
  173. /**
  174. * A date formatter to format a Date into a date in the format
  175. * "yyyy-MM-dd".
  176. */
  177. protected SimpleDateFormat fileDateFormatter = null;
  178. /**
  179. * The system timezone.
  180. */
  181. private TimeZone timezone = null;
  182. /**
  183. * The time zone offset relative to GMT in text form when daylight saving
  184. * is not in operation.
  185. */
  186. private String timeZoneNoDST = null;
  187. /**
  188. * The time zone offset relative to GMT in text form when daylight saving
  189. * is in operation.
  190. */
  191. private String timeZoneDST = null;
  192. /**
  193. * The current log file we are writing to. Helpful when checkExists
  194. * is true.
  195. */
  196. protected File currentLogFile = null;
  197. private static class AccessDateStruct {
  198. private Date currentDate = new Date();
  199. private String currentDateString = null;
  200. private SimpleDateFormat dayFormatter = new SimpleDateFormat("dd");
  201. private SimpleDateFormat monthFormatter = new SimpleDateFormat("MM");
  202. private SimpleDateFormat yearFormatter = new SimpleDateFormat("yyyy");
  203. private SimpleDateFormat timeFormatter = new SimpleDateFormat("HH:mm:ss");
  204. public AccessDateStruct() {
  205. TimeZone tz = TimeZone.getDefault();
  206. dayFormatter.setTimeZone(tz);
  207. monthFormatter.setTimeZone(tz);
  208. yearFormatter.setTimeZone(tz);
  209. timeFormatter.setTimeZone(tz);
  210. }
  211. }
  212. /**
  213. * The system time when we last updated the Date that this valve
  214. * uses for log lines.
  215. */
  216. private static final ThreadLocal<AccessDateStruct> currentDateStruct =
  217. new ThreadLocal<AccessDateStruct>() {
  218. @Override
  219. protected AccessDateStruct initialValue() {
  220. return new AccessDateStruct();
  221. }
  222. };
  223. /**
  224. * Resolve hosts.
  225. */
  226. private boolean resolveHosts = false;
  227. /**
  228. * Instant when the log daily rotation was last checked.
  229. */
  230. private volatile long rotationLastChecked = 0L;
  231. /**
  232. * Do we check for log file existence? Helpful if an external
  233. * agent renames the log file so we can automagically recreate it.
  234. */
  235. private boolean checkExists = false;
  236. /**
  237. * Are we doing conditional logging. default false.
  238. */
  239. protected String condition = null;
  240. /**
  241. * Date format to place in log file name. Use at your own risk!
  242. */
  243. protected String fileDateFormat = null;
  244. /**
  245. * Array of AccessLogElement, they will be used to make log message.
  246. */
  247. protected AccessLogElement[] logElements = null;
  248. // ------------------------------------------------------------- Properties
  249. /**
  250. * @return Returns the enabled.
  251. */
  252. public boolean getEnabled() {
  253. return enabled;
  254. }
  255. /**
  256. * @param enabled
  257. * The enabled to set.
  258. */
  259. public void setEnabled(boolean enabled) {
  260. this.enabled = enabled;
  261. }
  262. /**
  263. * Return the directory in which we create log files.
  264. */
  265. public String getDirectory() {
  266. return (directory);
  267. }
  268. /**
  269. * Set the directory in which we create log files.
  270. *
  271. * @param directory The new log file directory
  272. */
  273. public void setDirectory(String directory) {
  274. this.directory = directory;
  275. }
  276. /**
  277. * Return descriptive information about this implementation.
  278. */
  279. @Override
  280. public String getInfo() {
  281. return (info);
  282. }
  283. /**
  284. * Return the format pattern.
  285. */
  286. public String getPattern() {
  287. return (this.pattern);
  288. }
  289. /**
  290. * Set the format pattern, first translating any recognized alias.
  291. *
  292. * @param pattern The new pattern
  293. */
  294. public void setPattern(String pattern) {
  295. if (pattern == null)
  296. pattern = "";
  297. if (pattern.equals(Constants.AccessLog.COMMON_ALIAS))
  298. pattern = Constants.AccessLog.COMMON_PATTERN;
  299. if (pattern.equals(Constants.AccessLog.COMBINED_ALIAS))
  300. pattern = Constants.AccessLog.COMBINED_PATTERN;
  301. this.pattern = pattern;
  302. logElements = createLogElements();
  303. }
  304. /**
  305. * Check for file existence before logging.
  306. */
  307. public boolean isCheckExists() {
  308. return checkExists;
  309. }
  310. /**
  311. * Set whether to check for log file existence before logging.
  312. *
  313. * @param checkExists true meaning to check for file existence.
  314. */
  315. public void setCheckExists(boolean checkExists) {
  316. this.checkExists = checkExists;
  317. }
  318. /**
  319. * Return the log file prefix.
  320. */
  321. public String getPrefix() {
  322. return (prefix);
  323. }
  324. /**
  325. * Set the log file prefix.
  326. *
  327. * @param prefix The new log file prefix
  328. */
  329. public void setPrefix(String prefix) {
  330. this.prefix = prefix;
  331. }
  332. /**
  333. * Should we rotate the logs
  334. */
  335. public boolean isRotatable() {
  336. return rotatable;
  337. }
  338. /**
  339. * Set the value is we should we rotate the logs
  340. *
  341. * @param rotatable true is we should rotate.
  342. */
  343. public void setRotatable(boolean rotatable) {
  344. this.rotatable = rotatable;
  345. }
  346. /**
  347. * Is the logging buffered
  348. */
  349. public boolean isBuffered() {
  350. return buffered;
  351. }
  352. /**
  353. * Set the value if the logging should be buffered
  354. *
  355. * @param buffered true if buffered.
  356. */
  357. public void setBuffered(boolean buffered) {
  358. this.buffered = buffered;
  359. }
  360. /**
  361. * Return the log file suffix.
  362. */
  363. public String getSuffix() {
  364. return (suffix);
  365. }
  366. /**
  367. * Set the log file suffix.
  368. *
  369. * @param suffix The new log file suffix
  370. */
  371. public void setSuffix(String suffix) {
  372. this.suffix = suffix;
  373. }
  374. /**
  375. * Set the resolve hosts flag.
  376. *
  377. * @param resolveHosts The new resolve hosts value
  378. */
  379. public void setResolveHosts(boolean resolveHosts) {
  380. this.resolveHosts = resolveHosts;
  381. }
  382. /**
  383. * Get the value of the resolve hosts flag.
  384. */
  385. public boolean isResolveHosts() {
  386. return resolveHosts;
  387. }
  388. /**
  389. * Return whether the attribute name to look for when
  390. * performing conditional logging. If null, every
  391. * request is logged.
  392. */
  393. public String getCondition() {
  394. return condition;
  395. }
  396. /**
  397. * Set the ServletRequest.attribute to look for to perform
  398. * conditional logging. Set to null to log everything.
  399. *
  400. * @param condition Set to null to log everything
  401. */
  402. public void setCondition(String condition) {
  403. this.condition = condition;
  404. }
  405. /**
  406. * Return the date format date based log rotation.
  407. */
  408. public String getFileDateFormat() {
  409. return fileDateFormat;
  410. }
  411. /**
  412. * Set the date format date based log rotation.
  413. */
  414. public void setFileDateFormat(String fileDateFormat) {
  415. this.fileDateFormat = fileDateFormat;
  416. }
  417. // --------------------------------------------------------- Public Methods
  418. /**
  419. * Execute a periodic task, such as reloading, etc. This method will be
  420. * invoked inside the classloading context of this container. Unexpected
  421. * throwables will be caught and logged.
  422. */
  423. @Override
  424. public void backgroundProcess() {
  425. if (getState().isAvailable() && getEnabled() && writer != null &&
  426. buffered) {
  427. writer.flush();
  428. }
  429. }
  430. /**
  431. * Log a message summarizing the specified request and response, according
  432. * to the format specified by the <code>pattern</code> property.
  433. *
  434. * @param request Request being processed
  435. * @param response Response being processed
  436. *
  437. * @exception IOException if an input/output error has occurred
  438. * @exception ServletException if a servlet error has occurred
  439. */
  440. @Override
  441. public void invoke(Request request, Response response) throws IOException,
  442. ServletException {
  443. final String t1Name = AccessLogValve.class.getName()+".t1";
  444. if (getState().isAvailable() && getEnabled()) {
  445. // Pass this request on to the next valve in our pipeline
  446. long t1 = System.currentTimeMillis();
  447. boolean asyncdispatch = request.isAsyncDispatching();
  448. if (!asyncdispatch) {
  449. request.setAttribute(t1Name, new Long(t1));
  450. }
  451. getNext().invoke(request, response);
  452. //we're not done with the request
  453. if (request.isAsyncDispatching()) {
  454. return;
  455. } else if (asyncdispatch && request.getAttribute(t1Name)!=null) {
  456. t1 = ((Long)request.getAttribute(t1Name)).longValue();
  457. }
  458. long t2 = System.currentTimeMillis();
  459. long time = t2 - t1;
  460. log(request,response, time);
  461. } else
  462. getNext().invoke(request, response);
  463. }
  464. public void log(Request request, Response response, long time) {
  465. if (logElements == null || condition != null
  466. && null != request.getRequest().getAttribute(condition)) {
  467. return;
  468. }
  469. Date date = getDate();
  470. StringBuilder result = new StringBuilder(128);
  471. for (int i = 0; i < logElements.length; i++) {
  472. logElements[i].addElement(result, date, request, response, time);
  473. }
  474. log(result.toString());
  475. }
  476. /**
  477. * Rename the existing log file to something else. Then open the
  478. * old log file name up once again. Intended to be called by a JMX
  479. * agent.
  480. *
  481. *
  482. * @param newFileName The file name to move the log file entry to
  483. * @return true if a file was rotated with no error
  484. */
  485. public synchronized boolean rotate(String newFileName) {
  486. if (currentLogFile != null) {
  487. File holder = currentLogFile;
  488. close();
  489. try {
  490. holder.renameTo(new File(newFileName));
  491. } catch (Throwable e) {
  492. log.error("rotate failed", e);
  493. }
  494. /* Make sure date is correct */
  495. dateStamp = fileDateFormatter.format(
  496. new Date(System.currentTimeMillis()));
  497. open();
  498. return true;
  499. } else {
  500. return false;
  501. }
  502. }
  503. // -------------------------------------------------------- Private Methods
  504. /**
  505. * Close the currently open log file (if any)
  506. */
  507. private synchronized void close() {
  508. if (writer == null) {
  509. return;
  510. }
  511. writer.flush();
  512. writer.close();
  513. writer = null;
  514. dateStamp = "";
  515. currentLogFile = null;
  516. }
  517. /**
  518. * Log the specified message to the log file, switching files if the date
  519. * has changed since the previous log call.
  520. *
  521. * @param message Message to be logged
  522. */
  523. public void log(String message) {
  524. if (rotatable) {
  525. // Only do a logfile switch check once a second, max.
  526. long systime = System.currentTimeMillis();
  527. if ((systime - rotationLastChecked) > 1000) {
  528. synchronized(this) {
  529. if ((systime - rotationLastChecked) > 1000) {
  530. rotationLastChecked = systime;
  531. String tsDate;
  532. // Check for a change of date
  533. tsDate = fileDateFormatter.format(new Date(systime));
  534. // If the date has changed, switch log files
  535. if (!dateStamp.equals(tsDate)) {
  536. close();
  537. dateStamp = tsDate;
  538. open();
  539. }
  540. }
  541. }
  542. }
  543. }
  544. /* In case something external rotated the file instead */
  545. if (checkExists) {
  546. synchronized (this) {
  547. if (currentLogFile != null && !currentLogFile.exists()) {
  548. try {
  549. close();
  550. } catch (Throwable e) {
  551. log.info("at least this wasn't swallowed", e);
  552. }
  553. /* Make sure date is correct */
  554. dateStamp = fileDateFormatter.format(
  555. new Date(System.currentTimeMillis()));
  556. open();
  557. }
  558. }
  559. }
  560. // Log this message
  561. synchronized(this) {
  562. if (writer != null) {
  563. writer.println(message);
  564. if (!buffered) {
  565. writer.flush();
  566. }
  567. }
  568. }
  569. }
  570. /**
  571. * Return the month abbreviation for the specified month, which must
  572. * be a two-digit String.
  573. *
  574. * @param month Month number ("01" .. "12").
  575. */
  576. private String lookup(String month) {
  577. int index;
  578. try {
  579. index = Integer.parseInt(month) - 1;
  580. } catch (Throwable t) {
  581. index = 0; // Can not happen, in theory
  582. }
  583. return (months[index]);
  584. }
  585. /**
  586. * Open the new log file for the date specified by <code>dateStamp</code>.
  587. */
  588. protected synchronized void open() {
  589. // Create the directory if necessary
  590. File dir = new File(directory);
  591. if (!dir.isAbsolute())
  592. dir = new File(System.getProperty("catalina.base"), directory);
  593. dir.mkdirs();
  594. // Open the current log file
  595. try {
  596. String pathname;
  597. // If no rotate - no need for dateStamp in fileName
  598. if (rotatable) {
  599. pathname = dir.getAbsolutePath() + File.separator + prefix
  600. + dateStamp + suffix;
  601. } else {
  602. pathname = dir.getAbsolutePath() + File.separator + prefix
  603. + suffix;
  604. }
  605. writer = new PrintWriter(new BufferedWriter(new FileWriter(
  606. pathname, true), 128000), false);
  607. currentLogFile = new File(pathname);
  608. } catch (IOException e) {
  609. writer = null;
  610. currentLogFile = null;
  611. }
  612. }
  613. /**
  614. * This method returns a Date object that is accurate to within one second.
  615. * If a thread calls this method to get a Date and it's been less than 1
  616. * second since a new Date was created, this method simply gives out the
  617. * same Date again so that the system doesn't spend time creating Date
  618. * objects unnecessarily.
  619. *
  620. * @return Date
  621. */
  622. private Date getDate() {
  623. // Only create a new Date once per second, max.
  624. long systime = System.currentTimeMillis();
  625. AccessDateStruct struct = currentDateStruct.get();
  626. if ((systime - struct.currentDate.getTime()) > 1000) {
  627. struct.currentDate.setTime(systime);
  628. struct.currentDateString = null;
  629. }
  630. return struct.currentDate;
  631. }
  632. private String getTimeZone(Date date) {
  633. if (timezone.inDaylightTime(date)) {
  634. return timeZoneDST;
  635. } else {
  636. return timeZoneNoDST;
  637. }
  638. }
  639. private String calculateTimeZoneOffset(long offset) {
  640. StringBuilder tz = new StringBuilder();
  641. if ((offset < 0)) {
  642. tz.append("-");
  643. offset = -offset;
  644. } else {
  645. tz.append("+");
  646. }
  647. long hourOffset = offset / (1000 * 60 * 60);
  648. long minuteOffset = (offset / (1000 * 60)) % 60;
  649. if (hourOffset < 10)
  650. tz.append("0");
  651. tz.append(hourOffset);
  652. if (minuteOffset < 10)
  653. tz.append("0");
  654. tz.append(minuteOffset);
  655. return tz.toString();
  656. }
  657. /**
  658. * Start this component and implement the requirements
  659. * of {@link LifecycleBase#startInternal()}.
  660. *
  661. * @exception LifecycleException if this component detects a fatal error
  662. * that prevents this component from being used
  663. */
  664. @Override
  665. protected synchronized void startInternal() throws LifecycleException {
  666. // Initialize the timeZone, Date formatters, and currentDate
  667. timezone = TimeZone.getDefault();
  668. timeZoneNoDST = calculateTimeZoneOffset(timezone.getRawOffset());
  669. int offset = timezone.getDSTSavings();
  670. timeZoneDST = calculateTimeZoneOffset(timezone.getRawOffset() + offset);
  671. if (fileDateFormat == null || fileDateFormat.length() == 0)
  672. fileDateFormat = "yyyy-MM-dd";
  673. fileDateFormatter = new SimpleDateFormat(fileDateFormat);
  674. fileDateFormatter.setTimeZone(timezone);
  675. dateStamp = fileDateFormatter.format(currentDateStruct.get().currentDate);
  676. open();
  677. setState(LifecycleState.STARTING);
  678. }
  679. /**
  680. * Stop this component and implement the requirements
  681. * of {@link LifecycleBase#stopInternal()}.
  682. *
  683. * @exception LifecycleException if this component detects a fatal error
  684. * that prevents this component from being used
  685. */
  686. @Override
  687. protected synchronized void stopInternal() throws LifecycleException {
  688. setState(LifecycleState.STOPPING);
  689. close();
  690. }
  691. /**
  692. * AccessLogElement writes the partial message into the buffer.
  693. */
  694. protected interface AccessLogElement {
  695. public void addElement(StringBuilder buf, Date date, Request request,
  696. Response response, long time);
  697. }
  698. /**
  699. * write thread name - %I
  700. */
  701. protected class ThreadNameElement implements AccessLogElement {
  702. public void addElement(StringBuilder buf, Date date, Request request,
  703. Response response, long time) {
  704. RequestInfo info = request.getCoyoteRequest().getRequestProcessor();
  705. if(info != null) {
  706. buf.append(info.getWorkerThreadName());
  707. } else {
  708. buf.append("-");
  709. }
  710. }
  711. }
  712. /**
  713. * write local IP address - %A
  714. */
  715. protected static class LocalAddrElement implements AccessLogElement {
  716. private static final String LOCAL_ADDR_VALUE;
  717. static {
  718. String init;
  719. try {
  720. init = InetAddress.getLocalHost().getHostAddress();
  721. } catch (Throwable e) {
  722. init = "127.0.0.1";
  723. }
  724. LOCAL_ADDR_VALUE = init;
  725. }
  726. public void addElement(StringBuilder buf, Date date, Request request,
  727. Response response, long time) {
  728. buf.append(LOCAL_ADDR_VALUE);
  729. }
  730. }
  731. /**
  732. * write remote IP address - %a
  733. */
  734. protected class RemoteAddrElement implements AccessLogElement {
  735. public void addElement(StringBuilder buf, Date date, Request request,
  736. Response response, long time) {
  737. buf.append(request.getRemoteAddr());
  738. }
  739. }
  740. /**
  741. * write remote host name - %h
  742. */
  743. protected class HostElement implements AccessLogElement {
  744. public void addElement(StringBuilder buf, Date date, Request request,
  745. Response response, long time) {
  746. buf.append(request.getRemoteHost());
  747. }
  748. }
  749. /**
  750. * write remote logical username from identd (always returns '-') - %l
  751. */
  752. protected class LogicalUserNameElement implements AccessLogElement {
  753. public void addElement(StringBuilder buf, Date date, Request request,
  754. Response response, long time) {
  755. buf.append('-');
  756. }
  757. }
  758. /**
  759. * write request protocol - %H
  760. */
  761. protected class ProtocolElement implements AccessLogElement {
  762. public void addElement(StringBuilder buf, Date date, Request request,
  763. Response response, long time) {
  764. buf.append(request.getProtocol());
  765. }
  766. }
  767. /**
  768. * write remote user that was authenticated (if any), else '-' - %u
  769. */
  770. protected class UserElement implements AccessLogElement {
  771. public void addElement(StringBuilder buf, Date date, Request request,
  772. Response response, long time) {
  773. if (request != null) {
  774. String value = request.getRemoteUser();
  775. if (value != null) {
  776. buf.append(value);
  777. } else {
  778. buf.append('-');
  779. }
  780. } else {
  781. buf.append('-');
  782. }
  783. }
  784. }
  785. /**
  786. * write date and time, in Common Log Format - %t
  787. */
  788. protected class DateAndTimeElement implements AccessLogElement {
  789. public void addElement(StringBuilder buf, Date date, Request request,
  790. Response response, long time) {
  791. AccessDateStruct struct = currentDateStruct.get();
  792. if (struct.currentDateString == null) {
  793. StringBuilder current = new StringBuilder(32);
  794. current.append('[');
  795. current.append(struct.dayFormatter.format(date));
  796. current.append('/');
  797. current.append(lookup(struct.monthFormatter.format(date)));
  798. current.append('/');
  799. current.append(struct.yearFormatter.format(date));
  800. current.append(':');
  801. current.append(struct.timeFormatter.format(date));
  802. current.append(' ');
  803. current.append(getTimeZone(date));
  804. current.append(']');
  805. struct.currentDateString = current.toString();
  806. }
  807. buf.append(struct.currentDateString);
  808. }
  809. }
  810. /**
  811. * write first line of the request (method and request URI) - %r
  812. */
  813. protected class RequestElement implements AccessLogElement {
  814. public void addElement(StringBuilder buf, Date date, Request request,
  815. Response response, long time) {
  816. if (request != null) {
  817. buf.append(request.getMethod());
  818. buf.append(' ');
  819. buf.append(request.getRequestURI());
  820. if (request.getQueryString() != null) {
  821. buf.append('?');
  822. buf.append(request.getQueryString());
  823. }
  824. buf.append(' ');
  825. buf.append(request.getProtocol());
  826. } else {
  827. buf.append("- - ");
  828. }
  829. }
  830. }
  831. /**
  832. * write HTTP status code of the response - %s
  833. */
  834. protected class HttpStatusCodeElement implements AccessLogElement {
  835. public void addElement(StringBuilder buf, Date date, Request request,
  836. Response response, long time) {
  837. if (response != null) {
  838. buf.append(response.getStatus());
  839. } else {
  840. buf.append('-');
  841. }
  842. }
  843. }
  844. /**
  845. * write local port on which this request was received - %p
  846. */
  847. protected class LocalPortElement implements AccessLogElement {
  848. public void addElement(StringBuilder buf, Date date, Request request,
  849. Response response, long time) {
  850. buf.append(request.getServerPort());
  851. }
  852. }
  853. /**
  854. * write bytes sent, excluding HTTP headers - %b, %B
  855. */
  856. protected class ByteSentElement implements AccessLogElement {
  857. private boolean conversion;
  858. /**
  859. * if conversion is true, write '-' instead of 0 - %b
  860. */
  861. public ByteSentElement(boolean conversion) {
  862. this.conversion = conversion;
  863. }
  864. public void addElement(StringBuilder buf, Date date, Request request,
  865. Response response, long time) {
  866. long length = response.getContentCountLong() ;
  867. if (length <= 0 && conversion) {
  868. buf.append('-');
  869. } else {
  870. buf.append(length);
  871. }
  872. }
  873. }
  874. /**
  875. * write request method (GET, POST, etc.) - %m
  876. */
  877. protected class MethodElement implements AccessLogElement {
  878. public void addElement(StringBuilder buf, Date date, Request request,
  879. Response response, long time) {
  880. if (request != null) {
  881. buf.append(request.getMethod());
  882. }
  883. }
  884. }
  885. /**
  886. * write time taken to process the request - %D, %T
  887. */
  888. protected class ElapsedTimeElement implements AccessLogElement {
  889. private boolean millis;
  890. /**
  891. * if millis is true, write time in millis - %D
  892. * if millis is false, write time in seconds - %T
  893. */
  894. public ElapsedTimeElement(boolean millis) {
  895. this.millis = millis;
  896. }
  897. public void addElement(StringBuilder buf, Date date, Request request,
  898. Response response, long time) {
  899. if (millis) {
  900. buf.append(time);
  901. } else {
  902. // second
  903. buf.append(time / 1000);
  904. buf.append('.');
  905. int remains = (int) (time % 1000);
  906. buf.append(remains / 100);
  907. remains = remains % 100;
  908. buf.append(remains / 10);
  909. buf.append(remains % 10);
  910. }
  911. }
  912. }
  913. /**
  914. * write Query string (prepended with a '?' if it exists) - %q
  915. */
  916. protected class QueryElement implements AccessLogElement {
  917. public void addElement(StringBuilder buf, Date date, Request request,
  918. Response response, long time) {
  919. String query = null;
  920. if (request != null)
  921. query = request.getQueryString();
  922. if (query != null) {
  923. buf.append('?');
  924. buf.append(query);
  925. }
  926. }
  927. }
  928. /**
  929. * write user session ID - %S
  930. */
  931. protected class SessionIdElement implements AccessLogElement {
  932. public void addElement(StringBuilder buf, Date date, Request request,
  933. Response response, long time) {
  934. if (request != null) {
  935. if (request.getSession(false) != null) {
  936. buf.append(request.getSessionInternal(false)
  937. .getIdInternal());
  938. } else {
  939. buf.append('-');
  940. }
  941. } else {
  942. buf.append('-');
  943. }
  944. }
  945. }
  946. /**
  947. * write requested URL path - %U
  948. */
  949. protected class RequestURIElement implements AccessLogElement {
  950. public void addElement(StringBuilder buf, Date date, Request request,
  951. Response response, long time) {
  952. if (request != null) {
  953. buf.append(request.getRequestURI());
  954. } else {
  955. buf.append('-');
  956. }
  957. }
  958. }
  959. /**
  960. * write local server name - %v
  961. */
  962. protected class LocalServerNameElement implements AccessLogElement {
  963. public void addElement(StringBuilder buf, Date date, Request request,
  964. Response response, long time) {
  965. buf.append(request.getServerName());
  966. }
  967. }
  968. /**
  969. * write any string
  970. */
  971. protected class StringElement implements AccessLogElement {
  972. private String str;
  973. public StringElement(String str) {
  974. this.str = str;
  975. }
  976. public void addElement(StringBuilder buf, Date date, Request request,
  977. Response response, long time) {
  978. buf.append(str);
  979. }
  980. }
  981. /**
  982. * write incoming headers - %{xxx}i
  983. */
  984. protected class HeaderElement implements AccessLogElement {
  985. private String header;
  986. public HeaderElement(String header) {
  987. this.header = header;
  988. }
  989. public void addElement(StringBuilder buf, Date date, Request request,
  990. Response response, long time) {
  991. String value = request.getHeader(header);
  992. if (value == null) {
  993. buf.append('-');
  994. } else {
  995. buf.append(value);
  996. }
  997. }
  998. }
  999. /**
  1000. * write a specific cookie - %{xxx}c
  1001. */
  1002. protected class CookieElement implements AccessLogElement {
  1003. private String header;
  1004. public CookieElement(String header) {
  1005. this.header = header;
  1006. }
  1007. public void addElement(StringBuilder buf, Date date, Request request,
  1008. Response response, long time) {
  1009. String value = "-";
  1010. Cookie[] c = request.getCookies();
  1011. if (c != null) {
  1012. for (int i = 0; i < c.length; i++) {
  1013. if (header.equals(c[i].getName())) {
  1014. value = c[i].getValue();
  1015. break;
  1016. }
  1017. }
  1018. }
  1019. buf.append(value);
  1020. }
  1021. }
  1022. /**
  1023. * write a specific response header - %{xxx}o
  1024. */
  1025. protected class ResponseHeaderElement implements AccessLogElement {
  1026. private String header;
  1027. public ResponseHeaderElement(String header) {
  1028. this.header = header;
  1029. }
  1030. public void addElement(StringBuilder buf, Date date, Request request,
  1031. Response response, long time) {
  1032. if (null != response) {
  1033. Iterator<String> iter = response.getHeaders(header).iterator();
  1034. boolean first = true;
  1035. while (iter.hasNext()) {
  1036. if (!first) {
  1037. buf.append(",");
  1038. }
  1039. buf.append(iter.next());
  1040. }
  1041. return ;
  1042. }
  1043. buf.append("-");
  1044. }
  1045. }
  1046. /**
  1047. * write an attribute in the ServletRequest - %{xxx}r
  1048. */
  1049. protected class RequestAttributeElement implements AccessLogElement {
  1050. private String header;
  1051. public RequestAttributeElement(String header) {
  1052. this.header = header;
  1053. }
  1054. public void addElement(StringBuilder buf, Date date, Request request,
  1055. Response response, long time) {
  1056. Object value = null;
  1057. if (request != null) {
  1058. value = request.getAttribute(header);
  1059. } else {
  1060. value = "??";
  1061. }
  1062. if (value != null) {
  1063. if (value instanceof String) {
  1064. buf.append((String) value);
  1065. } else {
  1066. buf.append(value.toString());
  1067. }
  1068. } else {
  1069. buf.append('-');
  1070. }
  1071. }
  1072. }
  1073. /**
  1074. * write an attribute in the HttpSession - %{xxx}s
  1075. */
  1076. protected class SessionAttributeElement implements AccessLogElement {
  1077. private String header;
  1078. public SessionAttributeElement(String header) {
  1079. this.header = header;
  1080. }
  1081. public void addElement(StringBuilder buf, Date date, Request request,
  1082. Response response, long time) {
  1083. Object value = null;
  1084. if (null != request) {
  1085. HttpSession sess = request.getSession(false);
  1086. if (null != sess)
  1087. value = sess.getAttribute(header);
  1088. } else {
  1089. value = "??";
  1090. }
  1091. if (value != null) {
  1092. if (value instanceof String) {
  1093. buf.append((String) value);
  1094. } else {
  1095. buf.append(value.toString());
  1096. }
  1097. } else {
  1098. buf.append('-');
  1099. }
  1100. }
  1101. }
  1102. /**
  1103. * parse pattern string and create the array of AccessLogElement
  1104. */
  1105. protected AccessLogElement[] createLogElements() {
  1106. List<AccessLogElement> list = new ArrayList<AccessLogElement>();
  1107. boolean replace = false;
  1108. StringBuilder buf = new StringBuilder();
  1109. for (int i = 0; i < pattern.length(); i++) {
  1110. char ch = pattern.charAt(i);
  1111. if (replace) {
  1112. /*
  1113. * For code that processes {, the behavior will be ... if I do
  1114. * not encounter a closing } - then I ignore the {
  1115. */
  1116. if ('{' == ch) {
  1117. StringBuilder name = new StringBuilder();
  1118. int j = i + 1;
  1119. for (; j < pattern.length() && '}' != pattern.charAt(j); j++) {
  1120. name.append(pattern.charAt(j));
  1121. }
  1122. if (j + 1 < pattern.length()) {
  1123. /* the +1 was to account for } which we increment now */
  1124. j++;
  1125. list.add(createAccessLogElement(name.toString(),
  1126. pattern.charAt(j)));
  1127. i = j; /* Since we walked more than one character */
  1128. } else {
  1129. // D'oh - end of string - pretend we never did this
  1130. // and do processing the "old way"
  1131. list.add(createAccessLogElement(ch));
  1132. }
  1133. } else {
  1134. list.add(createAccessLogElement(ch));
  1135. }
  1136. replace = false;
  1137. } else if (ch == '%') {
  1138. replace = true;
  1139. list.add(new StringElement(buf.toString()));
  1140. buf = new StringBuilder();
  1141. } else {
  1142. buf.append(ch);
  1143. }
  1144. }
  1145. if (buf.length() > 0) {
  1146. list.add(new StringElement(buf.toString()));
  1147. }
  1148. return list.toArray(new AccessLogElement[0]);
  1149. }
  1150. /**
  1151. * create an AccessLogElement implementation which needs header string
  1152. */
  1153. private AccessLogElement createAccessLogElement(String header, char pattern) {
  1154. switch (pattern) {
  1155. case 'i':
  1156. return new HeaderElement(header);
  1157. case 'c':
  1158. return new CookieElement(header);
  1159. case 'o':
  1160. return new ResponseHeaderElement(header);
  1161. case 'r':
  1162. return new RequestAttributeElement(header);
  1163. case 's':
  1164. return new SessionAttributeElement(header);
  1165. default:
  1166. return new StringElement("???");
  1167. }
  1168. }
  1169. /**
  1170. * create an AccessLogElement implementation
  1171. */
  1172. private AccessLogElement createAccessLogElement(char pattern) {
  1173. switch (pattern) {
  1174. case 'a':
  1175. return new RemoteAddrElement();
  1176. case 'A':
  1177. return new LocalAddrElement();
  1178. case 'b':
  1179. return new ByteSentElement(true);
  1180. case 'B':
  1181. return new ByteSentElement(false);
  1182. case 'D':
  1183. return new ElapsedTimeElement(true);
  1184. case 'h':
  1185. return new HostElement();
  1186. case 'H':
  1187. return new ProtocolElement();
  1188. case 'l':
  1189. return new LogicalUserNameElement();
  1190. case 'm':
  1191. return new MethodElement();
  1192. case 'p':
  1193. return new LocalPortElement();
  1194. case 'q':
  1195. return new QueryElement();
  1196. case 'r':
  1197. return new RequestElement();
  1198. case 's':
  1199. return new HttpStatusCodeElement();
  1200. case 'S':
  1201. return new SessionIdElement();
  1202. case 't':
  1203. return new DateAndTimeElement();
  1204. case 'T':
  1205. return new ElapsedTimeElement(false);
  1206. case 'u':
  1207. return new UserElement();
  1208. case 'U':
  1209. return new RequestURIElement();
  1210. case 'v':
  1211. return new LocalServerNameElement();
  1212. case 'I':
  1213. return new ThreadNameElement();
  1214. default:
  1215. return new StringElement("???" + pattern + "???");
  1216. }
  1217. }
  1218. }