All Downloads are FREE. Search and download functionalities are using the official Maven repository.

me.chanjar.tomcat.valves.MongoAccessLogValve Maven / Gradle / Ivy

The newest version!
package me.chanjar.tomcat.valves;

import java.io.IOException;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Scanner;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpSession;

import org.apache.catalina.AccessLog;
import org.apache.catalina.Globals;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.Session;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.util.RequestUtil;
import org.apache.catalina.valves.Constants;
import org.apache.catalina.valves.ValveBase;
import org.apache.coyote.RequestInfo;
import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.res.StringManager;

import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.WriteConcern;


/**
 * Documentation: Github
 * @author Dianiel Qian([email protected])
 *
 */
public class MongoAccessLogValve extends ValveBase implements AccessLog {

  private static final Log log = LogFactory.getLog(MongoAccessLogValve.class);

  /**
   * The string manager for this package.
   */
  protected static final StringManager sm = StringManager.getManager(me.chanjar.tomcat.valves.Constants.Package);
  
  //------------------------------------------------------ Constructor
  public MongoAccessLogValve() {
      super(true);
  }

  // ----------------------------------------------------- Instance Variables


  /**
   * The descriptive information about this implementation.
   */
  protected static final String info = "chanjarster.tomcat.valves.MongoAccessLogValve/0.1";


  /**
   * The pattern used to format our access log lines.
   */
  protected String pattern = null;

  /**
   * The system time when we last updated the Date that this valve
   * uses for log lines.
   */
  private static final ThreadLocal localDate = new ThreadLocal() {
      @Override
      protected Date initialValue() {
          return new Date();
      }
  };

  /**
   * Resolve hosts.
   */
  private boolean resolveHosts = false;

  /**
   * Are we doing conditional logging. default null.
   * It is the value of conditionUnless property.
   */
  protected String condition = null;

  /**
   * Are we doing conditional logging. default null.
   * It is the value of conditionIf property.
   */
  protected String conditionIf = null;


  /**
   * Don't log the request matches the patterns
   */
  protected String excludes = ".js,.css,jpg,.jpeg,.gif,.png,.bmp,.gif,.html,.htm";
  
  protected String[] excludePatterns = {".js", ".css", ".jpeg", ".jpg", ".gif", ".png", ".bmp", ".gif", ".html", ".htm"};
  
  /**
   * MongoDB uri. See http://api.mongodb.org/java/current/com/mongodb/MongoClientURI.html
   */
  protected String uri;
  
  /**
   * Which db to store access logs. Default is tomcat
   */
  protected String dbName = "tomcat";
  
  /**
   * Which Collection to store access logs. Default is tomcat_access_logs
   */
  protected String collName = "tomcat_access_logs";
  
  /**
   * Should we rotate our log collection? Default is true (like old behavior)
   * if true then use capped collection
   */
  protected boolean rotatable = true;
  
  /**
   * MongoDB collection's max size(in megabytes), Default is 1024
   */
  protected int capSize = 1024;
  
  /**
   * MongoDB collection instance
   */
  protected DBCollection coll = null;
  
  /**
   * Array of AccessLogElement, they will be used to make log message.
   */
  protected AccessLogElement[] logElements = null;

  /**
   * @see #setRequestAttributesEnabled(boolean)
   */
  protected boolean requestAttributesEnabled = false;

  /**
   * If true, then record the error page 
   */
  protected boolean recordError = true;
  
  // ------------------------------------------------------------- Properties

  /**
   * {@inheritDoc}
   */
  @Override
  public void setRequestAttributesEnabled(boolean requestAttributesEnabled) {
      this.requestAttributesEnabled = requestAttributesEnabled;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public boolean getRequestAttributesEnabled() {
      return requestAttributesEnabled;
  }

  /**
   * Return descriptive information about this implementation.
   */
  @Override
  public String getInfo() {
      return (info);
  }


  /**
   * Return the format pattern.
   */
  public String getPattern() {
      return (this.pattern);
  }


  /**
   * Set the format pattern, first translating any recognized alias.
   *
   * @param pattern The new pattern
   */
  public void setPattern(String pattern) {
      if (pattern == null) {
          this.pattern = "";
      } else if (pattern.equals(Constants.AccessLog.COMMON_ALIAS)) {
          this.pattern = Constants.AccessLog.COMMON_PATTERN;
      } else if (pattern.equals(Constants.AccessLog.COMBINED_ALIAS)) {
          this.pattern = Constants.AccessLog.COMBINED_PATTERN;
      } else if (pattern.equals(me.chanjar.tomcat.valves.Constants.MongoAccessLog.DEFAULT_ALIAS)) {
          this.pattern = me.chanjar.tomcat.valves.Constants.MongoAccessLog.DEFAULT_PATTERN;
      } else if (pattern.equals(me.chanjar.tomcat.valves.Constants.MongoAccessLog.ALL_ALIAS)) {
        this.pattern = me.chanjar.tomcat.valves.Constants.MongoAccessLog.ALL_PATTERN;
      } else {
          this.pattern = pattern;
      }
      logElements = createLogElements();
  }


  /**
   * Should we rotate the logs
   */
  public boolean isRotatable() {
      return rotatable;
  }


  /**
   * Set the value is we should we rotate the logs
   *
   * @param rotatable true is we should rotate.
   */
  public void setRotatable(boolean rotatable) {
      this.rotatable = rotatable;
  }

  
  /**
   * Set the resolve hosts flag.
   *
   * @param resolveHosts The new resolve hosts value
   * @deprecated Unused, removed in Tomcat 8.
   * See org.apache.catalina.connector.Connector.setEnableLookups(boolean).
   */
  @Deprecated
  public void setResolveHosts(boolean resolveHosts) {
      this.resolveHosts = resolveHosts;
  }


  /**
   * Get the value of the resolve hosts flag.
   * @deprecated Unused, removed in Tomcat 8.
   * See org.apache.catalina.connector.Connector.setEnableLookups(boolean).
   */
  @Deprecated
  public boolean isResolveHosts() {
      return resolveHosts;
  }


  /**
   * Return whether the attribute name to look for when
   * performing conditional logging. If null, every
   * request is logged.
   */
  public String getCondition() {
      return condition;
  }


  /**
   * Set the ServletRequest.attribute to look for to perform
   * conditional logging. Set to null to log everything.
   *
   * @param condition Set to null to log everything
   */
  public void setCondition(String condition) {
      this.condition = condition;
  }


  /**
   * Return whether the attribute name to look for when
   * performing conditional logging. If null, every
   * request is logged.
   */
  public String getConditionUnless() {
      return getCondition();
  }


  /**
   * Set the ServletRequest.attribute to look for to perform
   * conditional logging. Set to null to log everything.
   *
   * @param condition Set to null to log everything
   */
  public void setConditionUnless(String condition) {
      setCondition(condition);
  }

  /**
   * Return whether the attribute name to look for when
   * performing conditional logging. If null, every
   * request is logged.
   */
  public String getConditionIf() {
      return conditionIf;
  }


  /**
   * Set the ServletRequest.attribute to look for to perform
   * conditional logging. Set to null to log everything.
   *
   * @param condition Set to null to log everything
   */
  public void setConditionIf(String condition) {
      this.conditionIf = condition;
  }

  // --------------------------------------------------------- Public Methods

  /**
   * Execute a periodic task, such as reloading, etc. This method will be
   * invoked inside the classloading context of this container. Unexpected
   * throwables will be caught and logged.
   */
  @Override
  public synchronized void backgroundProcess() {
      // do nothing
  }

  /**
   * Log a message summarizing the specified request and response, according
   * to the format specified by the pattern property.
   *
   * @param request Request being processed
   * @param response Response being processed
   *
   * @exception IOException if an input/output error has occurred
   * @exception ServletException if a servlet error has occurred
   */
  @Override
  public void invoke(Request request, Response response) throws IOException,
          ServletException {
      getNext().invoke(request, response);
  }


  @Override
  public void log(Request request, Response response, long time) {
      if (!getState().isAvailable() || logElements == null
              || condition != null
              && null != request.getRequest().getAttribute(condition)
              || conditionIf != null
              && null == request.getRequest().getAttribute(conditionIf)) {
          return;
      }
      String uri = request.getRequestURI();
      if (uri != null) {
        for(String pattern : this.excludePatterns) {
          if (uri.indexOf(pattern) != -1) {
            return;
          }
        }
      }
      /**
       * XXX This is a bit silly, but we want to have start and stop time and
       * duration consistent. It would be better to keep start and stop
       * simply in the request and/or response object and remove time
       * (duration) from the interface.
       */
      long start = request.getCoyoteRequest().getStartTime();
      Date date = getDate(start + time);

      StringBuilder buf = new StringBuilder();
      DBObject result = new BasicDBObject(10);
      for (int i = 0; i < logElements.length; i++) {
          logElements[i].addElement(buf, result, date, request, response, time);
      }

      log(result);
  }


  // -------------------------------------------------------- Private Methods
  /**
   * Close the currently open mongodb connection (if any)
   *
   */
  private synchronized void close() {
    if (this.coll == null) {
      return;
    }
    
    try {
      this.coll.getDB().getMongo().close();
    } catch (Exception ex) {
      log.error(sm.getString("mongoAccessLogValve.closeConnectionError"), ex);
    } finally {
      this.coll = null;
    }
  }


  /**
   * Log the specified message to the log file, switching files if the date
   * has changed since the previous log call.
   *
   * @param message Message to be logged
   */
  public void log(DBObject log) {
      /* In case something external rotated the file instead */
      synchronized (this) {
          open();
      }
      this.coll.insert(log, WriteConcern.UNACKNOWLEDGED);
  }


  /**
   * Open the new log file for the date specified by dateStamp.
   */
  protected synchronized void open() {
    if (coll != null) {
      return;
    }
    
    this.coll = CollectionFactory.getOrCreateCollection(this.uri, this.dbName, this.collName, this.rotatable, this.capSize, this.log, this.sm);
    
    
  }

  /**
   * This method returns a ThreadLocal Date object that is set to the
   * specified time. This saves creating a new Date object for every request.
   *
   * @return Date
   */
  private static Date getDate(long systime) {
      Date date = localDate.get();
      date.setTime(systime);
      return date;
  }
  
  /**
   * Find a locale by name
   */
  protected static Locale findLocale(String name, Locale fallback) {
      if (name == null || name.isEmpty()) {
          return Locale.getDefault();
      } else {
          for (Locale l: Locale.getAvailableLocales()) {
              if (name.equals(l.toString())) {
                  return(l);
              }
          }
      }
      log.error(sm.getString("mongoAccessLogValve.invalidLocale", name));
      return fallback;
  }


  /**
   * Start this component and implement the requirements
   * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
   *
   * @exception LifecycleException if this component detects a fatal error
   *  that prevents this component from being used
   */
  @Override
  protected synchronized void startInternal() throws LifecycleException {
      open();
      setState(LifecycleState.STARTING);
  }


  /**
   * Stop this component and implement the requirements
   * of {@link org.apache.catalina.util.LifecycleBase#stopInternal()}.
   *
   * @exception LifecycleException if this component detects a fatal error
   *  that prevents this component from being used
   */
  @Override
  protected synchronized void stopInternal() throws LifecycleException {
      setState(LifecycleState.STOPPING);
      close();
  }

  /**
   * AccessLogElement writes the partial message into the buffer.
   */
  protected interface AccessLogElement {
      public void addElement(StringBuilder buf, DBObject result, Date date, Request request,
              Response response, long time);

  }

  /**
   * write thread name - %I { "thread" : "xxx" }
   */
  protected static class ThreadNameElement implements AccessLogElement {
      @Override
      public void addElement(StringBuilder buf, DBObject result, Date date, Request request,
              Response response, long time) {
          RequestInfo info = request.getCoyoteRequest().getRequestProcessor();
          if(info != null) {
            result.put("thread", info.getWorkerThreadName());
          } else {
            result.put("thread","-");
          }
      }
  }

  /**
   * write local IP address - %A { "localIP" : "xxx" }
   */
  protected static class LocalAddrElement implements AccessLogElement {

      private static final String LOCAL_ADDR_VALUE;

      static {
          String init;
          try {
              init = InetAddress.getLocalHost().getHostAddress();
          } catch (Throwable e) {
              ExceptionUtils.handleThrowable(e);
              init = "127.0.0.1";
          }
          LOCAL_ADDR_VALUE = init;
      }

      @Override
      public void addElement(StringBuilder buf, DBObject result, Date date, Request request,
              Response response, long time) {
        result.put("localIP", LOCAL_ADDR_VALUE);
      }
  }

  /**
   * write remote IP address - %a { "remoteIP" : "xxx" }
   */
  protected class RemoteAddrElement implements AccessLogElement {
      @Override
      public void addElement(StringBuilder buf, DBObject result, Date date, Request request,
              Response response, long time) {
          if (requestAttributesEnabled) {
              Object addr = request.getAttribute(REMOTE_ADDR_ATTRIBUTE);
              if (addr == null) {
                result.put("remoteIP", request.getRemoteAddr());
              } else {
                result.put("remoteIP", addr);
              }
          } else {
            result.put("remoteIP", request.getRemoteAddr());
          }
      }
  }

  /**
   * write remote host name - %h { "remoteHost" : "xxx" }
   */
  protected class HostElement implements AccessLogElement {
      @Override
      public void addElement(StringBuilder buf, DBObject result, Date date, Request request,
              Response response, long time) {
          String value = null;
          if (requestAttributesEnabled) {
              Object host = request.getAttribute(REMOTE_HOST_ATTRIBUTE);
              if (host != null) {
                  value = host.toString();
              }
          }
          if (value == null || value.length() == 0) {
              value = request.getRemoteHost();
          }
          if (value == null || value.length() == 0) {
              value = "-";
          }
          result.put("remoteHost", value);
      }
  }

  /**
   * write remote logical username from identd (always returns '-') - %l { "user" : "xxx" }
   */
  protected static class LogicalUserNameElement implements AccessLogElement {
      @Override
      public void addElement(StringBuilder buf, DBObject result, Date date, Request request,
              Response response, long time) {
          result.put("user", '-');
      }
  }

  /**
   * write request protocol - %H { "protocol" : "xxx" }
   */
  protected class ProtocolElement implements AccessLogElement {
      @Override
      public void addElement(StringBuilder buf, DBObject result, Date date, Request request,
              Response response, long time) {
          if (requestAttributesEnabled) {
              Object proto = request.getAttribute(PROTOCOL_ATTRIBUTE);
              if (proto == null) {
                  result.put("protocol", request.getProtocol());
              } else {
                result.put("protocol", proto);
              }
          } else {
            result.put("protocol", request.getProtocol());
          }
      }
  }

  /**
   * write remote user that was authenticated (if any), else '-' - %u { "remoteUser" : "xxx" }
   */
  protected static class UserElement implements AccessLogElement {
      @Override
      public void addElement(StringBuilder buf, DBObject result, Date date, Request request,
              Response response, long time) {
          if (request != null) {
              String value = request.getRemoteUser();
              if (value != null) {
                  result.put("remoteUser", value);
              } else {
                  result.put("remoteUser", '-');
              }
          } else {
              result.put("remoteUser", '-');
          }
      }
  }

  /**
   * write date and time - %t or %t{format},  { "datetime" : "xxx" }
   */
  protected class DateAndTimeElement implements AccessLogElement {

      /* Whether to use begin of request or end of response as the timestamp */
      private boolean usesBegin = false;

      protected DateAndTimeElement() {
          this(null);
      }

      protected DateAndTimeElement(String header) {
      }

      @Override
      public void addElement(StringBuilder buf, DBObject result, Date date, Request request,
              Response response, long time) {
          long timestamp = date.getTime();
          if (usesBegin) {
              timestamp -= time;
          }
          result.put("datetime", new Date(timestamp));
      }
  }

  /**
   * write first line of the request (method and request URI) - %r { "line1st" : "xxx" }
   */
  protected static class RequestElement implements AccessLogElement {
      @Override
      public void addElement(StringBuilder buf, DBObject result, Date date, Request request,
              Response response, long time) {
          if (request != null) {
              String method = request.getMethod();
              if (method == null) {
                  // No method means no request line
                  buf.append('-');
              } else {
                  buf.append(request.getMethod());
                  buf.append(' ');
                  buf.append(request.getRequestURI());
                  if (request.getQueryString() != null) {
                      buf.append('?');
                      buf.append(request.getQueryString());
                  }
                  buf.append(' ');
                  buf.append(request.getProtocol());
              }
          } else {
              buf.append('-');
          }
          result.put("line1st", buf.toString());
          buf.delete(0, buf.length());
      }
  }

  /**
   * write HTTP status code of the response - %s { "statusCode" : "xxx" }
   */
  protected static class HttpStatusCodeElement implements AccessLogElement {
      @Override
      public void addElement(StringBuilder buf, DBObject result, Date date, Request request,
              Response response, long time) {
          if (response != null) {
            result.put("statusCode", response.getStatus());
          } else {
            result.put("statusCode", '-');
          }
      }
  }

  /**
   * write local port on which this request was received - %p { "localPort" : xxx }
   */
  protected class LocalPortElement implements AccessLogElement {
      @Override
      public void addElement(StringBuilder buf, DBObject result, Date date, Request request,
              Response response, long time) {
          if (requestAttributesEnabled) {
              Object port = request.getAttribute(SERVER_PORT_ATTRIBUTE);
              if (port == null) {
                  result.put("localPort", request.getServerPort());
              } else {
                result.put("localPort", port);
              }
          } else {
            result.put("localPort", request.getServerPort());
          }
      }
  }

  /**
   * write bytes sent, excluding HTTP headers - %b, %B { "bytesSent" : xxx }
   */
  protected static class ByteSentElement implements AccessLogElement {
      private final boolean conversion;

      /**
       * if conversion is true, write '-' instead of 0 - %b
       */
      public ByteSentElement(boolean conversion) {
          this.conversion = conversion;
      }

      @Override
      public void addElement(StringBuilder buf, DBObject result, Date date, Request request,
              Response response, long time) {
          // Don't need to flush since trigger for log message is after the
          // response has been committed
          long length = response.getBytesWritten(false);
          if (length <= 0) {
              // Protect against nulls and unexpected types as these values
              // may be set by untrusted applications
              Object start = request.getAttribute(
                      Globals.SENDFILE_FILE_START_ATTR);
              if (start instanceof Long) {
                  Object end = request.getAttribute(
                          Globals.SENDFILE_FILE_END_ATTR);
                  if (end instanceof Long) {
                      length = ((Long) end).longValue() -
                              ((Long) start).longValue();
                  }
              }
          }
          if (length <= 0 && conversion) {
              result.put("bytesSent", '-');
          } else {
              result.put("bytesSent", length);
          }
      }
  }

  /**
   * write request method (GET, POST, etc.) - %m { "method" : "xxx" }
   */
  protected static class MethodElement implements AccessLogElement {
      @Override
      public void addElement(StringBuilder buf, DBObject result, Date date, Request request,
              Response response, long time) {
          if (request != null) {
            result.put("method", request.getMethod());
          }
      }
  }

  /**
   * write time taken to process the request - %D, %T { "elapsedMillis" : xxx } { "elapsedSeconds" : xxx }
   */
  protected static class ElapsedTimeElement implements AccessLogElement {
      private final boolean millis;

      /**
       * if millis is true, write time in millis - %D
       * if millis is false, write time in seconds - %T
       */
      public ElapsedTimeElement(boolean millis) {
          this.millis = millis;
      }

      @Override
      public void addElement(StringBuilder buf, DBObject result, Date date, Request request,
              Response response, long time) {
          if (millis) {
              result.put("elapsedMillis", time);
          } else {
              // second
              buf.append(time / 1000);
              buf.append('.');
              int remains = (int) (time % 1000);
              buf.append(remains / 100);
              remains = remains % 100;
              buf.append(remains / 10);
              buf.append(remains % 10);
              result.put("elapsedSeconds", Double.valueOf(buf.toString()));
              buf.delete(0, buf.length());
          }
      }
  }

  /**
   * write time until first byte is written (commit time) in millis - %F { "commitTime" : xxx }
   */
  protected static class FirstByteTimeElement implements AccessLogElement {
      @Override
      public void addElement(StringBuilder buf, DBObject result, Date date, Request request,
              Response response, long time) {
          long commitTime = response.getCoyoteResponse().getCommitTime();
          if (commitTime == -1) {
            result.put("commitTime", '-');
          } else {
              long delta =
                      commitTime - request.getCoyoteRequest().getStartTime();
              result.put("commitTime", delta);
          }
      }
  }

  /**
   * write Query string (prepended with a '?' if it exists) - %q { "queryString" : "xxx" }
   */
  protected static class QueryElement implements AccessLogElement {
      @Override
      public void addElement(StringBuilder buf, DBObject result, Date date, Request request,
              Response response, long time) {
          String query = null;
          if (request != null) {
              query = request.getQueryString();
          }
          if (query != null) {
              result.put("queryString", query);
          }
      }
  }

  /**
   * write All Parameters - %P { "params" : { "param1" : "xxx", "param2: "xxx" } }
* . will be replaced to $ if parameter name contains dot */ protected static class RequestParametersElement implements AccessLogElement { @Override public void addElement(StringBuilder buf, DBObject result, Date date, Request request, Response response, long time) { Enumeration paramNames = request.getParameterNames(); if (!paramNames.hasMoreElements()) { return; } DBObject params = new BasicDBObject(); while (paramNames.hasMoreElements()) { String paramName = paramNames.nextElement(); String replaced = paramName.replace('.', '$'); String[] values = request.getParameterValues(paramName); if(values.length == 0) { continue; } else if (values.length == 1){ params.put(replaced, values[0]); } else { params.put(replaced, values); } } result.put("params", params); } } /** * Record error message to { "error" : "xxxxx" } if error happends
* Code piece is copied from org.apache.catalina.valves.ErrorReportValve * @author qianjia * */ protected static class ErrorRecordElement implements AccessLogElement { @Override public void addElement(StringBuilder buf, DBObject result, Date date, Request request, Response response, long time) { Throwable throwable = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION); try { report(buf, result, request, response, throwable); } catch (Throwable tt) { ExceptionUtils.handleThrowable(tt); } finally { buf.delete(0, buf.length()); } } /** * Prints out an error report. * * @param request The request being processed * @param response The response being generated * @param throwable The exception that occurred (which possibly wraps * a root cause exception */ protected void report(StringBuilder sb, DBObject result,Request request, Response response, Throwable throwable) { // Do nothing on non-HTTP responses int statusCode = response.getStatus(); // Do nothing on a 1xx, 2xx and 3xx status and throwable is null if (throwable == null && statusCode < 400) { return; } String message = RequestUtil.filter(response.getMessage()); if (message == null) { if (throwable != null) { String exceptionMessage = throwable.getMessage(); if (exceptionMessage != null && exceptionMessage.length() > 0) { message = RequestUtil.filter( (new Scanner(exceptionMessage)).nextLine()); } } if (message == null) { message = ""; } } // Do nothing if there is no report for the specified status code and // no error message provided String report = null; StringManager smClient = StringManager.getManager(Constants.Package, request.getLocales()); try { report = smClient.getString("http." + statusCode); } catch (Throwable t) { ExceptionUtils.handleThrowable(t); } if (report == null) { if (message.length() == 0) { return; } else { report = smClient.getString("errorReportValve.noDescription"); } } sb.append(smClient.getString("errorReportValve.statusHeader", String.valueOf(statusCode), message)).append("\n"); sb.append("type "); if (throwable != null) { sb.append(smClient.getString("errorReportValve.exceptionReport")); } else { sb.append(smClient.getString("errorReportValve.statusReport")); } sb.append("\n"); sb.append(smClient.getString("errorReportValve.message")).append(": "); sb.append(message).append("\n"); sb.append(smClient.getString("errorReportValve.description")).append(": "); sb.append(report).append("\n"); if (throwable != null) { String stackTrace = getStackTraces(throwable); sb.append(RequestUtil.filter(stackTrace)).append("\n"); int loops = 0; Throwable rootCause = throwable.getCause(); while (rootCause != null && (loops < 10)) { stackTrace = getStackTraces(rootCause); sb.append(smClient.getString("errorReportValve.rootCause")).append("\n"); sb.append(RequestUtil.filter(stackTrace)).append("\n"); // In case root cause is somehow heavily nested rootCause = rootCause.getCause(); loops++; } } result.put("error", sb.toString()); } /** * Print out a partial servlet stack trace (truncating at the last * occurrence of javax.servlet.). */ protected String getStackTraces(Throwable t) { StringBuilder trace = new StringBuilder(); trace.append(t.toString()).append('\n'); StackTraceElement[] elements = t.getStackTrace(); for (int i = 0; i < elements.length; i++) { trace.append('\t').append(elements[i].toString()).append('\n'); } return trace.toString(); } } /** * write user session ID - %S { "sessionId" : "xxx" } */ protected static class SessionIdElement implements AccessLogElement { @Override public void addElement(StringBuilder buf, DBObject result, Date date, Request request, Response response, long time) { if (request == null) { result.put("sessionId", '-'); } else { Session session = request.getSessionInternal(false); if (session == null) { result.put("sessionId", '-'); } else { result.put("sessionId", session.getIdInternal()); } } } } /** * write requested URL path - %U { "url" : "xxx" } */ protected static class RequestURIElement implements AccessLogElement { @Override public void addElement(StringBuilder buf, DBObject result, Date date, Request request, Response response, long time) { if (request != null) { result.put("url", request.getRequestURI()); } else { result.put("url", '-'); } } } /** * write local server name - %v { "serverName" : "xxx" } */ protected static class LocalServerNameElement implements AccessLogElement { @Override public void addElement(StringBuilder buf, DBObject result, Date date, Request request, Response response, long time) { result.put("serverName", request.getServerName()); } } /** * write any string */ protected static class NoopElement implements AccessLogElement { public static final NoopElement INSTANCE = new NoopElement(); public NoopElement() { } @Override public void addElement(StringBuilder buf, DBObject result, Date date, Request request, Response response, long time) { } } /** * write incoming headers - %{xxx}i {"requestHeaders" : { "header1" : "xxx", "header2" : "xxx"} } */ protected static class HeaderElement implements AccessLogElement { private final String header; public HeaderElement(String header) { this.header = header; } @Override public void addElement(StringBuilder buf, DBObject result, Date date, Request request, Response response, long time) { Enumeration iter = request.getHeaders(header); if (iter.hasMoreElements()) { buf.append(iter.nextElement()); while (iter.hasMoreElements()) { buf.append(',').append(iter.nextElement()); } } else { buf.append('-'); } DBObject headers = (DBObject) result.get("requestHeaders"); if(headers == null) { headers = new BasicDBObject(); result.put("requestHeaders", headers); } headers.put(header, buf.toString()); buf.delete(0, buf.length()); } } /** * write a specific cookie - %{xxx}c {"cookies" : { "cookie1" : "xxx", "cookie2" : "xxx"} } */ protected static class CookieElement implements AccessLogElement { private final String header; public CookieElement(String header) { this.header = header; } @Override public void addElement(StringBuilder buf, DBObject result, Date date, Request request, Response response, long time) { String value = "-"; Cookie[] c = request.getCookies(); if (c != null) { for (int i = 0; i < c.length; i++) { if (header.equals(c[i].getName())) { value = c[i].getValue(); break; } } } DBObject cookies = (DBObject) result.get("cookies"); if(cookies == null) { cookies = new BasicDBObject(); result.put("cookies", cookies); } cookies.put(header, value); } } /** * write a specific response header - %{xxx}o {"responseHeaders" : { "header1" : "xxx", "header2" : "xxx"} } */ protected static class ResponseHeaderElement implements AccessLogElement { private final String header; public ResponseHeaderElement(String header) { this.header = header; } @Override public void addElement(StringBuilder buf, DBObject result, Date date, Request request, Response response, long time) { if (null != response) { Iterator iter = response.getHeaders(header).iterator(); if (iter.hasNext()) { buf.append(iter.next()); while (iter.hasNext()) { buf.append(',').append(iter.next()); } } else { buf.append('-'); } } else { buf.append('-'); } DBObject headers = (DBObject) result.get("responseHeaders"); if(headers == null) { headers = new BasicDBObject(); result.put("responseHeaders", headers); } headers.put(header, buf.toString()); buf.delete(0, buf.length()); } } /** * write an attribute in the ServletRequest - %{xxx}r {"requestAttrs" : { "attr1" : "xxx", "attr2" : "xxx"} } */ protected static class RequestAttributeElement implements AccessLogElement { private final String header; public RequestAttributeElement(String header) { this.header = header; } @Override public void addElement(StringBuilder buf, DBObject result, Date date, Request request, Response response, long time) { Object value = null; if (request != null) { value = request.getAttribute(header); } else { value = "??"; } if (value != null) { if (value instanceof String) { buf.append((String) value); } else { buf.append(value.toString()); } } else { buf.append('-'); } DBObject headers = (DBObject) result.get("requestAttrs"); if(headers == null) { headers = new BasicDBObject(); result.put("requestAttrs", headers); } headers.put(header, buf.toString()); buf.delete(0, buf.length()); } } /** * write an attribute in the HttpSession - %{xxx}s {"sessionAttrs" : { "attr1" : "xxx", "attr2" : "xxx"} } */ protected static class SessionAttributeElement implements AccessLogElement { private final String header; public SessionAttributeElement(String header) { this.header = header; } @Override public void addElement(StringBuilder buf, DBObject result, Date date, Request request, Response response, long time) { Object value = null; if (null != request) { HttpSession sess = request.getSession(false); if (null != sess) { value = sess.getAttribute(header); } } else { value = "??"; } if (value != null) { if (value instanceof String) { buf.append((String) value); } else { buf.append(value.toString()); } } else { buf.append('-'); } DBObject headers = (DBObject) result.get("sessionAttrs"); if(headers == null) { headers = new BasicDBObject(); result.put("sessionAttrs", headers); } headers.put(header, buf.toString()); buf.delete(0, buf.length()); } } /** * parse pattern string and create the array of AccessLogElement */ protected AccessLogElement[] createLogElements() { List list = new ArrayList(); boolean replace = false; StringBuilder buf = new StringBuilder(); for (int i = 0; i < pattern.length(); i++) { char ch = pattern.charAt(i); if (replace) { /* * For code that processes {, the behavior will be ... if I do * not encounter a closing } - then I ignore the { */ if ('{' == ch) { StringBuilder name = new StringBuilder(); int j = i + 1; for (; j < pattern.length() && '}' != pattern.charAt(j); j++) { name.append(pattern.charAt(j)); } if (j + 1 < pattern.length()) { /* the +1 was to account for } which we increment now */ j++; list.add(createAccessLogElement(name.toString(), pattern.charAt(j))); i = j; /* Since we walked more than one character */ } else { // D'oh - end of string - pretend we never did this // and do processing the "old way" list.add(createAccessLogElement(ch)); } } else { list.add(createAccessLogElement(ch)); } replace = false; } else if (ch == '%') { replace = true; list.add(NoopElement.INSTANCE); buf = new StringBuilder(); } else { buf.append(ch); } } if(this.recordError) { list.add(new ErrorRecordElement()); } if (buf.length() > 0) { list.add(new NoopElement()); } return list.toArray(new AccessLogElement[0]); } /** * create an AccessLogElement implementation which needs header string */ protected AccessLogElement createAccessLogElement(String header, char pattern) { switch (pattern) { case 'i': return new HeaderElement(header); case 'c': return new CookieElement(header); case 'o': return new ResponseHeaderElement(header); case 'r': return new RequestAttributeElement(header); case 's': return new SessionAttributeElement(header); case 't': return new DateAndTimeElement(header); default: return NoopElement.INSTANCE; } } /** * create an AccessLogElement implementation */ protected AccessLogElement createAccessLogElement(char pattern) { switch (pattern) { case 'a': return new RemoteAddrElement(); case 'A': return new LocalAddrElement(); case 'b': return new ByteSentElement(true); case 'B': return new ByteSentElement(false); case 'D': return new ElapsedTimeElement(true); case 'F': return new FirstByteTimeElement(); case 'h': return new HostElement(); case 'H': return new ProtocolElement(); case 'l': return new LogicalUserNameElement(); case 'm': return new MethodElement(); case 'p': return new LocalPortElement(); case 'q': return new QueryElement(); case 'r': return new RequestElement(); case 's': return new HttpStatusCodeElement(); case 'S': return new SessionIdElement(); case 't': return new DateAndTimeElement(); case 'T': return new ElapsedTimeElement(false); case 'u': return new UserElement(); case 'U': return new RequestURIElement(); case 'v': return new LocalServerNameElement(); case 'I': return new ThreadNameElement(); case 'P': return new RequestParametersElement(); default: return NoopElement.INSTANCE; } } public void setCapSize(int capSize) { this.capSize = capSize; } public void setCollName(String collName) { this.collName = collName; } public String getCollName() { return collName; } public void setExcludes(String excludes) { if(excludes == null) { return; } this.excludes = excludes; this.excludePatterns = excludes.split(","); } public void setRecordError(boolean recordError) { this.recordError = recordError; } public void setUri(String uri) { this.uri = uri; } public void setDbName(String dbName) { this.dbName = dbName; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy