httpd
Apache logging to MySQL
|
Apache by default logs to a file defined in the httpd[2].conf file. Then a tool such as webalizer can be used to display the logs on a webpage. That works really good for basic site statistic. However, if the records are logged to a database SQL analysis can be preformed very easily!
Here is how to make it happen:
-
Setup a database/table in MySQL for the log data. I am assuming you know a little bit about MySQL. Use "mysql" either by redirecting the following commands in the kernel's shell using the "mysql" command or by entering mysql's shell and enter the commands there.
CREATE DATABASE apache;
USE apache;
CREATE TABLE httpdlog
(
dt DATETIME NOT NULL, # request date
method VARCHAR(4) NOT NULL, # request method (GET, PUT, etc.)
request BLOB NOT NULL, # first line of request
url BLOB NOT NULL, # URL path
referer BLOB NOT NULL, # referer to current URL
status INT NOT NULL, # request status
size INT, # number of bytes transferred
time INT, # time taken to serve the request, in seconds
host VARCHAR(255) NOT NULL, # client host
agent VARCHAR(255) # user agent
)TYPE=MyISAM;
-
Use a program written in a language of your choice to log the records to the database. The following program example is written in C. Set the "#define"s to your system's values.
/* © 2004 lampware, inc. */
/* program written by kemiko (KEnt MIchael KOhlmeyer) of phx, az, usa */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "/usr/include/mysql/mysql.h"
#define HOSTNAME "hostname"
#define DATABASE "database"
#define USERNAME "username"
#define PASSWORD "password"
#define FILENAME "apache2mysql.log"
int main( int argc, char **argv )
{
FILE *fp = NULL;
char buffer[2048] = "";
char *field = 0;
char query[2048] = "";
int i = 0;
MYSQL mysql;
if( ! ( fp = fopen( FILENAME, "a" ) ) )
exit( EOF );
mysql_init( &mysql );
if( !mysql_real_connect( &mysql, HOSTNAME, USERNAME, PASSWORD, DATABASE, 0, NULL, 0 ) )
fprintf( fp, "Failed to connect to database: %s\n", mysql_error( &mysql ) );
sprintf( query, "INSERT INTO httpdlog VALUES(" );
fgets( buffer, sizeof( buffer ), stdin );
if( *buffer )
{
field = strtok( buffer, "\t" );
i = 1;
while( field != NULL )
{
if( strcmp( field, "\n" ) )
{
if( i > 1 )
strcat( query, "," );
if( i < 6 || i == 8 )
{
strcat( query, "'" );
strcat( query, field );
strcat( query, "'" );
}
else
{
strcat( query, field );
}
}
field = strtok( NULL, "\t" );
i++;
}
strcat( query, ")" );
if( mysql_query( &mysql, query ) )
fprintf( fp, "Failed to insert the record: %s\nThe failed query was: %s\n",
mysql_error( &mysql ), query );
}
fclose( fp );
}
-
Choose a directory for this program's executable to live in. I like an Apache directory since Apache will be the one using it. Make sure user "apache" or "httpd" can execute this executable.
-
Add the following two lines to the end of httpd[2].conf:
-
Restart httpd by running "service httpd restart" as "root" in the kernel's shell.
-
Run some SQL queries to see if the table is being populated and/or check the log file (#define FILENAME) for errors.
mysql> select count(*) from httpdlog;
mysql> select count(distinct host) from httpdlog;
mysql> select count(distinct url) from httpdlog;
mysql> select url, count(*) as count from httpdlog group by url order by count desc limit 10;
mysql> select min(dt), max(dt) from httpdlog;
mysql> select from_days(to_days(dt)) as day, count(*) from httpdlog group by day;
mysql> select hour(dt) as hour, count(*) from httpdlog group by hour;
mysql> select count(*)/(to_days(max(dt)) - to_days(min(dt)) + 1) from httpdlog;
mysql> select max(length(url)) from httpdlog;
mysql> select max(length(request)) from httpdlog;
mysql> select max(length(referer)) from httpdlog;
mysql> select dt, url, size, time from httpdlog where time > 0;
-
Write and use a nice little web interface to run these log queries. Below is example code.
web_usage.css
H1, H2, H3 {
FONT-STYLE: bold
}
BODY {
COLOR: #FFFFFF;
BACKGROUND-COLOR: #000000;
MARGIN-TOP: 0px;
MARGIN-LEFT: 0px;
MARGIN-RIGHT: 0px;
MARGIN-BOTTOM: 0px;
FONT-FAMILY: Tahoma, Arial;
FONT-SIZE: 14px;
}
TABLE {
BORDER: 0px;
FONT-FAMILY: Tahoma, Arial;
FONT-SIZE: 14pt;
}
TD {
FONT-FAMILY: Tahoma, Arial;
FONT-SIZE: 16px;
FONT-WEIGHT: bold;
}
A:LINK {
COLOR: #FFFFFF;
FONT-FAMILY: Tahoma, Arial;
FONT-WEIGHT: bold;
TEXT-DECORATION: none;
}
A:ACTIVE {
COLOR: #FFFFFF;
FONT-FAMILY: Tahoma, Arial;
FONT-WEIGHT: bold;
TEXT-DECORATION: none;
}
A:VISITED {
COLOR: #FFFFFF;
FONT-FAMILY: Tahoma, Arial;
FONT-WEIGHT: bold;
TEXT-DECORATION: none;
}
A:HOVER {
COLOR: #297082;
FONT-FAMILY: Tahoma, Arial;
FONT-WEIGHT: bold
TEXT-DECORATION: none;
}
web_usage.htm
<HTML>
<!-- © 2004 lampware, inc. -->
<!-- script written by kemiko (KEnt MIchael KOhlmeyer) of phx, az, usa -->
<HEAD>
<TITLE>
Web Usage
</TITLE>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<LINK REL="stylesheet" HREF="web_usage.css" TYPE="text/css">
</HEAD>
<BODY>
<TABLE WIDTH="100%" CELLPADDING="0" CELLSPACING="0" BORDER="0" BORDERCOLOR="000000">
<TR>
<TD>
<TABLE WIDTH="100%" BORDER="0" BORDERCOLOR="000000">
<TR>
<TD HEIGHT="25" ALIGN="CENTER" WIDTH="100%" BGCOLOR="155C6E">
<B>Web Usage</B>
</TD>
</TR>
</TABLE>
</TD>
</TR>
</TABLE>
<TABLE BORDER="0" CELLSPACING="5">
<TR>
<TD>
<B>
<TABLE BORDER="0" CELLSPACING="5">
<TR>
<TD NOWRAP="NOWRAP">
<A HREF="web_usage.php?query=SELECT * FROM httpdlog WHERE
date_format( dt, '%y' ) = date_format( curdate( ), '%y' ) AND
date_format( dt, '%m' ) = date_format( curdate( ), '%m' ) AND
date_format( dt, '%e' ) = date_format( curdate( ), '%e' )"><B>all
today</B></A>
</TD>
</TR>
<TR>
<TD NOWRAP="NOWRAP">
<A HREF="web_usage.php?query=SELECT dt, url, status, time,
host from httpdlog WHERE 32 <> SUBSTRING_INDEX( SUBSTRING_INDEX( host,
'.', 3 ), '.', -1 ) and 172 <> SUBSTRING_INDEX( SUBSTRING_INDEX( host,
'.', 1 ), '.', -1 )"><B>outside SBU</B></A>
</TD>
</TR>
<TR>
<TD NOWRAP="NOWRAP">
<A HREF="web_usage.php?query=SELECT dt, url, status, time,
size, host FROM httpdlog WHERE time > 0"><B>time > 0</B></A>
</TD>
</TR>
<TR>
<TD NOWRAP="NOWRAP">
<A HREF="web_usage.php?query=SELECT
SUBSTRING_INDEX(SUBSTRING_INDEX(url,'?',1),'.',-1) AS extension,
COUNT(size) AS requests, SUM(size) AS bytes, AVG(size) AS
'bytes/request' FROM httpdlog WHERE url LIKE '%.%' GROUP BY
extension"><B>by filetype</B></A>
</TD>
</TR>
<TR>
<TD NOWRAP="NOWRAP">
<A HREF="web_usage.php?query=SELECT dt, url, status, time,
host FROM httpdlog WHERE status <> 304 AND status <>
200"><B>errors</B></A>
</TD>
</TR>
<TR>
<TD NOWRAP="NOWRAP">
<A HREF="web_usage.php?query=SELECT url, count(url) AS
count FROM httpdlog GROUP BY url"><B>url</B></A>
</TD>
</TR>
<TR>
<TD NOWRAP="NOWRAP">
<A HREF="web_usage.php?query=SELECT url, count(url) AS
count FROM httpdlog WHERE url NOT LIKE '%InternetImages%' GROUP BY
url"><B>url minus images</B></A>
</TD>
</TR>
<TR>
<TD NOWRAP="NOWRAP">
<A HREF="web_usage.php?query=SELECT from_days(to_days(dt))
AS day, count(*) AS count FROM httpdlog GROUP BY day"><B>total hits by
day</B></A>
</TD>
</TR>
<TR>
<TD NOWRAP="NOWRAP">
<A HREF="web_usage.php?query=SELECT host, count(*) AS count
FROM httpdlog WHERE date_format( dt, '%y' ) = date_format( curdate( ),
'%y' ) AND date_format( dt, '%m' ) = date_format( curdate( ), '%m' )
AND date_format( dt, '%e' ) = date_format( curdate( ), '%e' ) GROUP BY
host"><B>today by host</B></A>
</TD>
</TR>
<TR>
<TD NOWRAP="NOWRAP">
<A HREF="web_usage.php?query=SELECT agent, host FROM
httpdlog WHERE agent LIKE '%Hotbar%' AND date_format( dt, '%y' ) =
date_format( curdate( ), '%y' ) AND date_format( dt, '%m' ) =
date_format( curdate( ), '%m' ) GROUP BY agent"><B>agents with
hotbar</B></A>
</TD>
</TR>
<TR>
<TD NOWRAP="NOWRAP">
<A HREF="web_usage.php?query=SELECT agent, host FROM
httpdlog WHERE agent LIKE '%Linux%' AND date_format( dt, '%y' ) =
date_format( curdate( ), '%y' ) AND date_format( dt, '%m' ) =
date_format( curdate( ), '%m' ) GROUP BY agent"><B>linux
agents</B></A>
</TD>
</TR>
<TR>
<TD NOWRAP="NOWRAP">
<A HREF="web_usage.php?query=SELECT * FROM httpdlog ORDER
BY dt DESC LIMIT 10;"><B>all limit 10</B></A>
</TD>
</TR>
<TR>
<TD NOWRAP="NOWRAP">
<A HREF="web_usage.php?query="><B></B></A>
</TD>
</TR>
</TABLE>
</B>
</TD>
</TR>
</TABLE>
</BODY>
</HTML>
web_usage.php
<HTML>
<!-- © 2004 lampware, inc. -->
<!-- script written by kemiko (KEnt MIchael KOhlmeyer) of phx, az, usa -->
<HEAD>
<TITLE>
<?
$query = str_replace( "\\", "", $query );
printf( " Web Usage: %s\n", $query );
?>
</TITLE>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<LINK REL="stylesheet" HREF="./sql_usage.css" TYPE="text/css">
</HEAD>
<BODY>
<?
$mysql_link = mysql_connect( "localhost", "readonly", "onlyread" );
mysql_select_db( "apache", $mysql_link );
print( " <TABLE BORDER=\"0\" WIDTH=\"100%\" CELLSPACING=\"0\" CELLPADDING=\"0\">\n" );
print( " <TR>\n" );
print( " <TD BGCOLOR=\"155C6E\" NOWRAP=\"NOWRAP\">\n" );
$query = str_replace( "\\", "", $query );
printf( " SQL Usage: %s\n", $query );
print( " </TD>\n" );
print( " </TR>\n " );
print( " </TABLE>\n" );
$mysql_result = mysql_query( $query, $mysql_link );
if( $mysql_result )
{
print( " <TABLE BORDER=\"0\" WIDTH=\"100%\" CELLSPACING=\"0\" CELLPADDING=\"0\">\n" );
print( " <TR>\n" );
while( $i < mysql_num_fields( $mysql_result ) )
{
print( " <TD BGCOLOR=\"000000\">\n" );
$field = mysql_fetch_field( $mysql_result );
printf( " %s\n", $field->name );
print( " </TD>\n" );
$i++;
}
$columns = $i;
print( " </TR>\n" );
$i = 0;
while( $row = mysql_fetch_row( $mysql_result ) )
{
print( " <TR>\n" );
if( $i % 2 )
$BGCOLOR = "156E53";
else
$BGCOLOR = "155C6E";
for( $j = 0; $j < mysql_num_fields( $mysql_result ); $j++ )
{
printf( " <TD BGCOLOR=\"$BGCOLOR\" NOWRAP=\"NOWRAP\">\n" );
if( $row[$j] || $row[$j] == 0 )
printf( " <FONT COLOR=\"%s\"> %s</FONT>\n", "FFFFFF", $row[$j] );
else
printf( " \n" );
printf( " </TD>\n" );
}
print( " </TR>\n" );
$i++;
}
$total = $i;
print( " <TR>\n" );
for( $i = 0; $i < $columns; $i++ )
{
print( " <TD BGCOLOR=\"000000\">\n" );
if( $i == 0 )
print( " total:\n" );
else if( $i == $columns - 1 )
print( " $total\n" );
else
print( " \n" );
print( " </TD>\n " );
}
print( " </TR>\n " );
print( " </TABLE>\n" );
}
else
{
printf( " <B>Failed to read the record(s): %s</B>\n", mysql_error( $mysql_link ) );
}
?>
</BODY>
</HTML>
|
|
|