JDBCAccessLogValvepublic 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.
|
Fields Summary |
---|
protected String | connectionNameThe connection username to use when trying to connect to the database. | protected String | connectionPasswordThe connection URL to use when trying to connect to the database. | protected Driver | driverInstance 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 | infoThe descriptive information about this implementation. | protected org.apache.catalina.util.LifecycleSupport | lifecycleThe lifecycle event support for this component. | private org.apache.catalina.util.StringManager | smThe string manager for this package. | private boolean | startedHas 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 void | addLifecycleListener(org.apache.catalina.LifecycleListener listener)Adds a Lifecycle listener.
lifecycle.addLifecycleListener(listener);
| protected void | close()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.String | getConnectionName()Return the username to use to connect to the database.
// ------------------------------------------------------------- Properties
return connectionName;
| public java.lang.String | getConnectionPassword()Return the password to use to connect to the database.
return connectionPassword;
| public long | getCurrentTimeMillis()
long systime = System.currentTimeMillis();
if ((systime - currentTimeMillis) > 1000) {
currentTimeMillis = new java.util.Date(systime).getTime();
}
return currentTimeMillis;
| public void | invoke(org.apache.catalina.connector.Request request, org.apache.catalina.connector.Response response)This method is invoked by Tomcat on each query.
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 void | open()Open (if necessary) and return a database connection for use by
this AccessLogValve.
// 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 void | removeLifecycleListener(org.apache.catalina.LifecycleListener listener)Removes a Lifecycle listener.
lifecycle.removeLifecycleListener(listener);
| public void | setBytesField(java.lang.String bytesField)Sets the name of the field containing the number of bytes returned.
this.bytesField = bytesField;
| public void | setConnectionName(java.lang.String connectionName)Set the username to use to connect to the database.
this.connectionName = connectionName;
| public void | setConnectionPassword(java.lang.String connectionPassword)Set the password to use to connect to the database.
this.connectionPassword = connectionPassword;
| public void | setConnectionURL(java.lang.String connectionURL)Sets the JDBC URL for the database where the log is stored.
this.connectionURL = connectionURL;
| public void | setDriverName(java.lang.String driverName)Sets the database driver name.
this.driverName = driverName;
| public void | setMethodField(java.lang.String methodField)Sets the name of the field containing the HTTP request method.
this.methodField = methodField;
| public void | setPattern(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.
this.pattern = pattern;
| public void | setQueryField(java.lang.String queryField)Sets the name of the field containing the URL part of the HTTP query.
this.queryField = queryField;
| public void | setRefererField(java.lang.String refererField)Sets the name of the field containing the referer.
this.refererField = refererField;
| public void | setRemoteHostField(java.lang.String remoteHostField)Sets the name of the field containing the remote host.
this.remoteHostField = remoteHostField;
| public void | setResolveHosts(java.lang.String resolveHosts)Determines whether IP host name resolution is done.
this.resolveHosts = new Boolean(resolveHosts).booleanValue();
| public void | setStatusField(java.lang.String statusField)Sets the name of the field containing the HTTP response status code.
this.statusField = statusField;
| public void | setTableName(java.lang.String tableName)Sets the name of the table where the logs are stored.
this.tableName = tableName;
| public void | setTimestampField(java.lang.String timestampField)Sets the name of the field containing the server-determined timestamp.
this.timestampField = timestampField;
| public void | setUserAgentField(java.lang.String userAgentField)Sets the name of the field containing the user agent.
this.userAgentField = userAgentField;
| public void | setUserField(java.lang.String userField)Sets the name of the field containing the remote user name.
this.userField = userField;
| public void | setVirtualHostField(java.lang.String virtualHostField)Sets the name of the field containing the virtual host information
(this is in fact the server name).
this.virtualHostField = virtualHostField;
| public void | start()Invoked by Tomcat on startup. The database connection is set here.
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 void | stop()Invoked by tomcat on shutdown. The database connection is closed here.
if (!started)
throw new LifecycleException
(sm.getString("accessLogValve.notStarted"));
lifecycle.fireLifecycleEvent(STOP_EVENT, null);
started = false;
close() ;
|
|