FileDocCategorySizeDatePackage
JDBCAccessLogValve.javaAPI DocApache Tomcat 6.0.1420988Fri Jul 20 04:20:34 BST 2007org.apache.catalina.valves

JDBCAccessLogValve

public final class JDBCAccessLogValve extends ValveBase implements org.apache.catalina.Lifecycle

This Tomcat extension logs server access directly to a database, and can be used instead of the regular file-based access log implemented in AccessLogValve. To use, copy into the server/classes directory of the Tomcat installation and configure in server.xml as:

<Valve className="org.apache.catalina.valves.JDBCAccessLogValve"
driverName="your_jdbc_driver"
connectionURL="your_jdbc_url"
pattern="combined" resolveHosts="false"
/>

Many parameters can be configured, such as the database connection (with driverName and connectionURL), the table name (tableName) and the field names (corresponding to the get/set method names). The same options as AccessLogValve are supported, such as resolveHosts and pattern ("common" or "combined" only).

When Tomcat is started, a database connection (with autoReconnect option) is created and used for all the log activity. When Tomcat is shutdown, the database connection is closed. This logger can be used at the level of the Engine context (being shared by all the defined hosts) or the Host context (one instance of the logger per host, possibly using different databases).

The database table can be created with the following command:

CREATE TABLE access (
id INT UNSIGNED AUTO_INCREMENT NOT NULL,
remoteHost CHAR(15) NOT NULL,
userName CHAR(15),
timestamp TIMESTAMP NOT NULL,
virtualHost VARCHAR(64) NOT NULL,
method VARCHAR(8) NOT NULL,
query VARCHAR(255) NOT NULL,
status SMALLINT UNSIGNED NOT NULL,
bytes INT UNSIGNED NOT NULL,
referer VARCHAR(128),
userAgent VARCHAR(128),
PRIMARY KEY (id),
INDEX (timestamp),
INDEX (remoteHost),
INDEX (virtualHost),
INDEX (query),
INDEX (userAgent)
);

If the table is created as above, its name and the field names don't need to be defined.

If the request method is "common", only these fields are used: remoteHost, user, timeStamp, query, status, bytes

TO DO: provide option for excluding logging of certain MIME types.

author
Andre de Jesus
author
Peter Rossbach

Fields Summary
protected String
connectionName
The connection username to use when trying to connect to the database.
protected String
connectionPassword
The connection URL to use when trying to connect to the database.
protected Driver
driver
Instance of the JDBC Driver class we use as a connection factory.
private String
driverName
private String
connectionURL
private String
tableName
private String
remoteHostField
private String
userField
private String
timestampField
private String
virtualHostField
private String
methodField
private String
queryField
private String
statusField
private String
bytesField
private String
refererField
private String
userAgentField
private String
pattern
private boolean
resolveHosts
private Connection
conn
private PreparedStatement
ps
private long
currentTimeMillis
protected static String
info
The descriptive information about this implementation.
protected org.apache.catalina.util.LifecycleSupport
lifecycle
The lifecycle event support for this component.
private org.apache.catalina.util.StringManager
sm
The string manager for this package.
private boolean
started
Has this component been started yet?
Constructors Summary
public JDBCAccessLogValve()
Class constructor. Initializes the fields with the default values. The defaults are:
driverName = null;
connectionURL = null;
tableName = "access";
remoteHostField = "remoteHost";
userField = "userName";
timestampField = "timestamp";
virtualHostField = "virtualHost";
methodField = "method";
queryField = "query";
statusField = "status";
bytesField = "bytes";
refererField = "referer";
userAgentField = "userAgent";
pattern = "common";
resolveHosts = false;

        super();
        driverName = null;
        connectionURL = null;
        tableName = "access";
        remoteHostField = "remoteHost";
        userField = "userName";
        timestampField = "timestamp";
        virtualHostField = "virtualHost";
        methodField = "method";
        queryField = "query";
        statusField = "status";
        bytesField = "bytes";
        refererField = "referer";
        userAgentField = "userAgent";
        pattern = "common";
        resolveHosts = false;
        conn = null;
        ps = null;
        currentTimeMillis = new java.util.Date().getTime();
    
Methods Summary
public voidaddLifecycleListener(org.apache.catalina.LifecycleListener listener)
Adds a Lifecycle listener.

param
listener The listener to add.


        lifecycle.addLifecycleListener(listener);

    
protected voidclose()
Close the specified database connection.


        // Do nothing if the database connection is already closed
        if (conn == null)
            return;

        // Close our prepared statements (if any)
        try {
            ps.close();
        } catch (Throwable f) {
            ;
        }
        this.ps = null;



        // Close this database connection, and log any errors
        try {
            conn.close();
        } catch (SQLException e) {
            container.getLogger().error(sm.getString("jdbcAccessLogValeve.close"), e); // Just log it here            
        } finally {
           this.conn = null;
        }

    
public org.apache.catalina.LifecycleListener[]findLifecycleListeners()
Get the lifecycle listeners associated with this lifecycle. If this Lifecycle has no listeners registered, a zero-length array is returned.


        return lifecycle.findLifecycleListeners();

    
public java.lang.StringgetConnectionName()
Return the username to use to connect to the database.



    // ------------------------------------------------------------- Properties
 
                   
       
        return connectionName;
    
public java.lang.StringgetConnectionPassword()
Return the password to use to connect to the database.

        return connectionPassword;
    
public longgetCurrentTimeMillis()

        long systime  =  System.currentTimeMillis();
        if ((systime - currentTimeMillis) > 1000) {
            currentTimeMillis  =  new java.util.Date(systime).getTime();
        }
        return currentTimeMillis;
    
public voidinvoke(org.apache.catalina.connector.Request request, org.apache.catalina.connector.Response response)
This method is invoked by Tomcat on each query.

param
request The Request object.
param
response The Response object.
exception
IOException Should not be thrown.
exception
ServletException Database SQLException is wrapped in a ServletException.


        getNext().invoke(request, response);

        String remoteHost = "";
        if(resolveHosts)
            remoteHost = request.getRemoteHost();
        else
            remoteHost = request.getRemoteAddr();
        String user = "";
        if(request != null)
            user = request.getRemoteUser();
        String query="";
        if(request != null)
            query = request.getRequestURI();
        int bytes = response.getContentCount();
        if(bytes < 0)
            bytes = 0;
        int status = response.getStatus();
        if (pattern.equals("combined")) {
                String virtualHost = "";
                if(request != null)
                    virtualHost = request.getServerName();
                String method = "";
                if(request != null)
                    method = request.getMethod();
                String referer = "";
                if(request != null)
                    referer = request.getHeader("referer");
                String userAgent = "";
                if(request != null)
                    userAgent = request.getHeader("user-agent");
        }
        synchronized (this) {
          int numberOfTries = 2;
          while (numberOfTries>0) {
            try {
                open();
    
                ps.setString(1, remoteHost);
                ps.setString(2, user);
                ps.setTimestamp(3, new Timestamp(getCurrentTimeMillis()));
                ps.setString(4, query);
                ps.setInt(5, status);
                ps.setInt(6, bytes);
                if (pattern.equals("combined")) {
     
                      String virtualHost = "";
                      if(request != null)
                         virtualHost = request.getServerName();
                      String method = "";
                      if(request != null)
                         method = request.getMethod();
                      String referer = "";
                      if(request != null)
                         referer = request.getHeader("referer");
                      String userAgent = "";
                      if(request != null)
                         userAgent = request.getHeader("user-agent");
                      ps.setString(7, virtualHost);
                      ps.setString(8, method);
                      ps.setString(9, referer);
                      ps.setString(10, userAgent);
                }
                ps.executeUpdate();
                return;
              } catch (SQLException e) {
                // Log the problem for posterity
                  container.getLogger().error(sm.getString("jdbcAccessLogValve.exception"), e);

                // Close the connection so that it gets reopened next time
                if (conn != null)
                    close();
              }
    	      numberOfTries--;        
           }
        }

    
protected voidopen()
Open (if necessary) and return a database connection for use by this AccessLogValve.

exception
SQLException if a database error occurs


        // Do nothing if there is a database connection already open
        if (conn != null)
            return ;

        // Instantiate our database driver if necessary
        if (driver == null) {
            try {
                Class clazz = Class.forName(driverName);
                driver = (Driver) clazz.newInstance();
            } catch (Throwable e) {
                throw new SQLException(e.getMessage());
            }
        }

        // Open a new connection
        Properties props = new Properties();
        props.put("autoReconnect", "true");
        if (connectionName != null)
            props.put("user", connectionName);
        if (connectionPassword != null)
            props.put("password", connectionPassword);
        conn = driver.connect(connectionURL, props);
        conn.setAutoCommit(true);
        if (pattern.equals("common")) {
                ps = conn.prepareStatement
                    ("INSERT INTO " + tableName + " (" 
                     + remoteHostField + ", " + userField + ", "
                     + timestampField +", " + queryField + ", "
                     + statusField + ", " + bytesField 
                     + ") VALUES(?, ?, ?, ?, ?, ?)");
        } else if (pattern.equals("combined")) {
                ps = conn.prepareStatement
                    ("INSERT INTO " + tableName + " (" 
                     + remoteHostField + ", " + userField + ", "
                     + timestampField + ", " + queryField + ", " 
                     + statusField + ", " + bytesField + ", " 
                     + virtualHostField + ", " + methodField + ", "
                     + refererField + ", " + userAgentField
                     + ") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
        }
    
public voidremoveLifecycleListener(org.apache.catalina.LifecycleListener listener)
Removes a Lifecycle listener.

param
listener The listener to remove.


        lifecycle.removeLifecycleListener(listener);

    
public voidsetBytesField(java.lang.String bytesField)
Sets the name of the field containing the number of bytes returned.

param
bytesField The name of the returned bytes field.

        this.bytesField = bytesField;
    
public voidsetConnectionName(java.lang.String connectionName)
Set the username to use to connect to the database.

param
connectionName Username

        this.connectionName = connectionName;
    
public voidsetConnectionPassword(java.lang.String connectionPassword)
Set the password to use to connect to the database.

param
connectionPassword User password

        this.connectionPassword = connectionPassword;
    
public voidsetConnectionURL(java.lang.String connectionURL)
Sets the JDBC URL for the database where the log is stored.

param
connectionURL The JDBC URL of the database.

        this.connectionURL = connectionURL;
    
public voidsetDriverName(java.lang.String driverName)
Sets the database driver name.

param
driverName The complete name of the database driver class.

        this.driverName = driverName;
    
public voidsetMethodField(java.lang.String methodField)
Sets the name of the field containing the HTTP request method.

param
methodField The name of the HTTP request method field.

        this.methodField = methodField;
    
public voidsetPattern(java.lang.String pattern)
Sets the logging pattern. The patterns supported correspond to the file-based "common" and "combined". These are translated into the use of tables containing either set of fields.

TO DO: more flexible field choices.

param
pattern The name of the logging pattern.

        this.pattern = pattern;
    
public voidsetQueryField(java.lang.String queryField)
Sets the name of the field containing the URL part of the HTTP query.

param
queryField The name of the field containing the URL part of the HTTP query.

        this.queryField = queryField;
    
public voidsetRefererField(java.lang.String refererField)
Sets the name of the field containing the referer.

param
refererField The referer field name.

        this.refererField = refererField;
    
public voidsetRemoteHostField(java.lang.String remoteHostField)
Sets the name of the field containing the remote host.

param
remoteHostField The name of the remote host field.

        this.remoteHostField = remoteHostField;
    
public voidsetResolveHosts(java.lang.String resolveHosts)
Determines whether IP host name resolution is done.

param
resolveHosts "true" or "false", if host IP resolution is desired or not.

        this.resolveHosts = new Boolean(resolveHosts).booleanValue();
    
public voidsetStatusField(java.lang.String statusField)
Sets the name of the field containing the HTTP response status code.

param
statusField The name of the HTTP response status code field.

        this.statusField = statusField;
    
public voidsetTableName(java.lang.String tableName)
Sets the name of the table where the logs are stored.

param
tableName The name of the table.

        this.tableName = tableName;
    
public voidsetTimestampField(java.lang.String timestampField)
Sets the name of the field containing the server-determined timestamp.

param
timestampField The name of the server-determined timestamp field.

        this.timestampField = timestampField;
    
public voidsetUserAgentField(java.lang.String userAgentField)
Sets the name of the field containing the user agent.

param
userAgentField The name of the user agent field.

        this.userAgentField = userAgentField;
    
public voidsetUserField(java.lang.String userField)
Sets the name of the field containing the remote user name.

param
userField The name of the remote user field.

        this.userField = userField;
    
public voidsetVirtualHostField(java.lang.String virtualHostField)
Sets the name of the field containing the virtual host information (this is in fact the server name).

param
virtualHostField The name of the virtual host field.

        this.virtualHostField = virtualHostField;
    
public voidstart()
Invoked by Tomcat on startup. The database connection is set here.

exception
LifecycleException Can be thrown on lifecycle inconsistencies or on database errors (as a wrapped SQLException).


        if (started)
            throw new LifecycleException
                (sm.getString("accessLogValve.alreadyStarted"));
        lifecycle.fireLifecycleEvent(START_EVENT, null);
        started = true;

        try {
            open() ;        
        } catch (SQLException e) {
            throw new LifecycleException(e);
        }

    
public voidstop()
Invoked by tomcat on shutdown. The database connection is closed here.

exception
LifecycleException Can be thrown on lifecycle inconsistencies or on database errors (as a wrapped SQLException).


        if (!started)
            throw new LifecycleException
                (sm.getString("accessLogValve.notStarted"));
        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
        started = false;
        
        close() ;