]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/log/DB/log_db_daemon.pl.in
Source Format Enforcement (#1234)
[thirdparty/squid.git] / src / log / DB / log_db_daemon.pl.in
index b3c90af3dab77c2bb3ca041514112fb6d9a87a48..e200d950c992af48f790b67146a94df456658733 100755 (executable)
@@ -14,195 +14,52 @@ $|=1;
 
 log_db_daemon - Database logging daemon for Squid
 
+Version 0.5.
+
 =head1 SYNOPSIS
 
 log_db_daemon DSN [options]
 
-=head1 DESCRIPTOIN
+=head1 DESCRIPTION
 
 This program writes Squid access.log entries to a database.
-Presently only accepts the B<squid> native format
-
-=over 8
-
-=item   B<DSN>
+Presently only accepts the B<squid> native log format.
 
-Database DSN encoded as a path. This is sent as the access_log file path.
+The script has been developed and tested in the following environment:
 
-Sample configuration:
-  access_log daemon:/host/database/table/username/password squid
+=over 4
 
-  to leave a parameter unspecified use a double slash:
-  access_log daemon://database/table/username/password squid
+=item squid-2.7 Squid-3.2
 
-Default "DBI:mysql:database=squid"
+=item mysql 5.0.26 and 5.1
 
-=item   B<--debug>
+=item perl 5.8.8
 
-Write debug messages to Squid stderr or cache.log
+=item OpenSUSE 10.2
 
 =back
 
-=cut
-
-# the first argument to this script is the log file path describing the DSN
-my $log_file = shift;
-
-# others may be options
-my $debug = 0;
-GetOptions(
-        'debug' => \$debug,
-        );
-
-
-# utility routine to print messages on stderr (so they appear in cache log)
-# without using warn, which would clutter the log with source line numbers
-sub log_info {
-    my $msg = shift;
-    print STDERR "$msg\n";
-}
-
-# we use logfile to pass database access information to this script
-# sample configuration:
-# access_log daemon:/host/database/table/username/password squid
-# to let a parmeter unspecified, e.g. the database host, use a double slash:
-# access_log daemon://database/table/username/password squid
-my ( $host, $database, $table, $user, $pass ) = $log_file =~ / \/(.*?)\/(.*?)\/(.*?)\/(.*?)\/(.*?) \z /xms;
-
-if ( !$host ) {
-    $host = 'localhost';
-    log_info("Database host not specified. Using $host.");
-}
-
-if ( !$database ) {
-    $database = 'squid_log';
-    log_info("Database name not specified. Using $database.");
-}
-
-if ( !$table ) {
-    $table = 'access_log';
-    log_info("Table parameter not specified. Using $table.");
-}
-
-if ( !$user ) {
-    $user = 'squid';
-    log_info("User parameter not specified. Using $user.");
-}
-
-if ( !$pass ) {
-    log_info('No password specified. Connecting with NO password.');
-}
-
-# fields that we should have in the table
-# Order here must match the order of fields in the Log format and parse() output array.
-my @db_fields = qw(
-    id
-    time_since_epoch
-    time_response
-    ip_client
-    squid_request_status
-    http_status_code
-    http_reply_size
-    http_method
-    http_url
-    http_username
-    squid_hier_status
-    ip_server
-    http_mime_type
-);
-
-# perform db connection
-my $dsn = "DBI:mysql:database=$database" . ($host ne "localhost" ? ":$host" : "");
-my $dbh;
-my $sth;
-eval {
-    warn "Connecting... dsn='$dsn', username='$user', password='...'";
-    $dbh = DBI->connect($dsn, $user, $pass, { AutoCommit => 1, RaiseError => 1, PrintError => 1 });
-};
-if ($EVAL_ERROR) {
-    die "Cannot connect to database: $DBI::errstr";
-}
-
-
-# a simple test to assure the specified table exists
-eval {
-    my $q = 'SELECT ' . join(',',@db_fields) . " FROM $table LIMIT 1";
-    my $sth = $dbh->prepare($q);
-    $sth->execute;
-};
-if ($EVAL_ERROR) {
-    # run a query to create the table of required syntax
-    my $create_query = 'CREATE TABLE ' . $table . ' (' .
-    " id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY," .
-    " time_since_epoch     DECIMAL(15,3)," .
-    " time_response        INTEGER," .
-    " ip_client            CHAR(15)," .
-    " ip_server            CHAR(15)," .
-    " http_status_code     VARCHAR(10)," .
-    " http_reply_size      INTEGER," .
-    " http_method          VARCHAR(20)," .
-    " http_url             TEXT," .
-    " http_username        VARCHAR(20)," .
-    " http_mime_type       VARCHAR(50)," .
-    " squid_request_status VARCHAR(50)," .
-    " squid_hier_status    VARCHAR(20)" .
-    ");" ;
+=head1 OPTIONS
 
-    $sth = $dbh->prepare($create_query);
-    $sth->execute;
-    # test again and fail hard if it is still broken.
-    eval {
-        my $q = 'SELECT ' . join(',',@db_fields) . " FROM $table LIMIT 1";
-        my $sth = $dbh->prepare($q);
-        $sth->execute;
-    };
-    if ($EVAL_ERROR) {
-        die "Error initializing database table: $EVAL_ERROR";
-    };
-}
-# test 
+=over 12
 
-# for better performance, prepare the statement at startup
-eval {
-    my $q = "INSERT INTO $table (" . join(',',@db_fields) . ") VALUES(NULL" . ',?' x (scalar(@db_fields)-1) . ')';
-    #$sth = $dbh->prepare("INSERT INTO $table VALUES(NULL,?,?,?,?,?,?,?,?,?,?,?,?)");
-    $sth = $dbh->prepare($q);
-};
-if ($EVAL_ERROR) {
-    die "Error while preparing sql statement: $EVAL_ERROR";
-}
-
-sub parse($) {
-    my ($line) = @_;
-    my (@t) = $line =~ /^L(\d+\.\d+) *(\d+?) (.*?) (.*?)\/(\d+?) (\d+?) (.*?) (.*?) (.*?) (.*?)\/(.*?) (.*)$/;
-}
-
-# main loop
-while (my $line = <>) {
-    chomp $line;
+=item   B<DSN>
 
-    my $cmd = substr($line, 0, 1);      # extract command byte
+Database DSN encoded as a path. This is sent as the access_log file path.
 
-    if ( $cmd eq 'L' ) {
-        my @log_entry = parse($line);
-        eval {                  # we catch db errors to avoid crashing squid in case something goes wrong...
-            $sth->execute(@log_entry) or die $sth->errstr
-        };
-        if ( $EVAL_ERROR ) {    # leave a trace of the error in the logs
-            warn $EVAL_ERROR . " values=(" . join(', ', @log_entry) . ')';
-        }
-    }
-}
+Sample configuration:
+  access_log daemon:/host/database/table/username/password squid
 
-$dbh->disconnect();
+  to leave a parameter unspecified use a double slash:
+  access_log daemon://database/table/username/password squid
 
-__END__
+Default "DBI:mysql:database=squid"
 
-=pod
+=item   B<--debug>
 
-=head1 DESCRIPTION
+Write debug info to stderr.
 
-This module exploits the new logfile daemon support available in squid 2.7 and 3.2 to store access log entries in a MySQL database.
+=back
 
 =head1 CONFIGURATION
 
@@ -300,24 +157,6 @@ The table created should look like:
     squid_request_status VARCHAR(20)
   );
 
-=head1 VERSION INFORMATION
-
-This document refers to C<log_db_daemon> script version 0.5.
-
-The script has been developed and tested in the following environment:
-
-=over 4
-
-=item squid-2.7 Squid-3.2
-
-=item mysql 5.0.26 and 5.1
-
-=item perl 5.8.8
-
-=item OpenSUSE 10.2
-
-=back
-
 =head1 DATA EXTRACTION
 
 =head2 Sample queries.
@@ -373,7 +212,7 @@ To distinguish only between HITs and MISSes:
     WHERE squid_request_status LIKE '%MISS%')
     /
     (SELECT COUNT(*) FROM access_log)*100
-    AS pecentage;
+    AS percentage;
 
 =item Response time ranges
 
@@ -421,19 +260,19 @@ To distinguish only between HITs and MISSes:
 
 =back
 
+=head1 KNOWN ISSUES
+
 =head2 Speed issues
 
 The MyISAM storage engine is known to be faster than the InnoDB one, so although it doesn't support transactions and referential integrity, it might be more appropriate in this scenario. You might want to append "ENGINE=MYISAM" at the end of the table creation code in the above SQL script.
 
 Indexes should be created according to the queries that are more frequently run. The DDL script only creates an implicit index for the primary key column.
 
-=head1 TODO
-
 =head2 Table cleanup
 
 This script currently implements only the C<L> (i.e. "append a line to the log") command, therefore the log lines are never purged from the table. This approach has an obvious scalability problem.
 
-One solution would be to implement e.g. the "rotate log" command in a way that would calculate some summary values, put them in a "summary table" and then delete the lines used to caluclate those values.
+One solution would be to implement e.g. the "rotate log" command in a way that would calculate some summary values, put them in a "summary table" and then delete the lines used to calculate those values.
 
 Similar cleanup code could be implemented in an external script and run periodically independently from squid log commands.
 
@@ -443,12 +282,13 @@ This script has only been tested in low-volume scenarios (single client, less th
 
 =head1 AUTHOR
 
-Marcello Romani, marcello.romani@libero.it
-Amos Jeffries, amosjeffries@squid-cache.org
+This program was written by
+I<Marcello Romani <marcello.romani@libero.it>> ,
+I<Amos Jeffries <amosjeffries@squid-cache.org>>
 
-=head1 COPYRIGHT AND LICENSE
+=head1 COPYRIGHT
 
- * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
+ * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
  *
  * Squid software is distributed under GPLv2+ license and includes
  * contributions from numerous individuals and organizations.
@@ -460,4 +300,180 @@ This library is free software; you can redistribute it and/or modify
 it under the same terms as Perl itself, either Perl version 5.8.8 or,
 at your option, any later version of Perl 5 you may have available.
 
+=head1 QUESTIONS
+
+Questions on the usage of this program can be sent to the I<Squid Users mailing list <squid-users@lists.squid-cache.org>>
+
+=head1 REPORTING BUGS
+
+Bug reports need to be made in English.
+See http://wiki.squid-cache.org/SquidFaq/BugReporting for details of what you need to include with your bug report.
+
+Report bugs or bug fixes using http://bugs.squid-cache.org/
+
+Report serious security bugs to I<Squid Bugs <squid-bugs@lists.squid-cache.org>>
+
+Report ideas for new improvements to the I<Squid Developers mailing list <squid-dev@lists.squid-cache.org>>
+
+=head1 SEE ALSO
+
+squid (8), GPL (7),
+
+The Squid FAQ wiki http://wiki.squid-cache.org/SquidFaq
+
+The Squid Configuration Manual http://www.squid-cache.org/Doc/config/
+
 =cut
+
+# the first argument to this script is the log file path describing the DSN
+my $log_file = shift;
+
+# others may be options
+my $debug = 0;
+GetOptions(
+    'debug' => \$debug,
+    );
+
+
+# utility routine to print messages on stderr (so they appear in cache log)
+# without using warn, which would clutter the log with source line numbers
+sub log_info {
+    my $msg = shift;
+    print STDERR "$msg\n";
+}
+
+# we use logfile to pass database access information to this script
+# sample configuration:
+# access_log daemon:/host/database/table/username/password squid
+# to let a parameter unspecified, e.g. the database host, use a double slash:
+# access_log daemon://database/table/username/password squid
+my ( $host, $database, $table, $user, $pass ) = $log_file =~ / \/(.*?)\/(.*?)\/(.*?)\/(.*?)\/(.*?) \z /xms;
+
+if ( !$host ) {
+    $host = 'localhost';
+    log_info("Database host not specified. Using $host.");
+}
+
+if ( !$database ) {
+    $database = 'squid_log';
+    log_info("Database name not specified. Using $database.");
+}
+
+if ( !$table ) {
+    $table = 'access_log';
+    log_info("Table parameter not specified. Using $table.");
+}
+
+if ( !$user ) {
+    $user = 'squid';
+    log_info("User parameter not specified. Using $user.");
+}
+
+if ( !$pass ) {
+    log_info('No password specified. Connecting with NO password.');
+}
+
+# fields that we should have in the table
+# Order here must match the order of fields in the Log format and parse() output array.
+my @db_fields = qw(
+    id
+    time_since_epoch
+    time_response
+    ip_client
+    squid_request_status
+    http_status_code
+    http_reply_size
+    http_method
+    http_url
+    http_username
+    squid_hier_status
+    ip_server
+    http_mime_type
+    );
+
+# perform db connection
+my $dsn = "DBI:mysql:database=$database" . ($host ne "localhost" ? ":$host" : "");
+my $dbh;
+my $sth;
+eval {
+    warn "Connecting... dsn='$dsn', username='$user', password='...'";
+    $dbh = DBI->connect($dsn, $user, $pass, { AutoCommit => 1, RaiseError => 1, PrintError => 1 });
+    };
+if ($EVAL_ERROR) {
+    die "Cannot connect to database: $DBI::errstr";
+}
+
+
+# a simple test to assure the specified table exists
+eval {
+    my $q = 'SELECT ' . join(',',@db_fields) . " FROM $table LIMIT 1";
+    my $sth = $dbh->prepare($q);
+    $sth->execute;
+    };
+if ($EVAL_ERROR) {
+    # run a query to create the table of required syntax
+    my $create_query = 'CREATE TABLE ' . $table . ' (' .
+        " id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY," .
+        " time_since_epoch     DECIMAL(15,3)," .
+        " time_response        INTEGER," .
+        " ip_client            CHAR(15)," .
+        " ip_server            CHAR(15)," .
+        " http_status_code     VARCHAR(10)," .
+        " http_reply_size      INTEGER," .
+        " http_method          VARCHAR(20)," .
+        " http_url             TEXT," .
+        " http_username        VARCHAR(20)," .
+        " http_mime_type       VARCHAR(50)," .
+        " squid_request_status VARCHAR(50)," .
+        " squid_hier_status    VARCHAR(20)" .
+        ");" ;
+
+    $sth = $dbh->prepare($create_query);
+    $sth->execute;
+    # test again and fail hard if it is still broken.
+    eval {
+        my $q = 'SELECT ' . join(',',@db_fields) . " FROM $table LIMIT 1";
+        my $sth = $dbh->prepare($q);
+        $sth->execute;
+        };
+    if ($EVAL_ERROR) {
+        die "Error initializing database table: $EVAL_ERROR";
+    };
+}
+# test
+
+# for better performance, prepare the statement at startup
+eval {
+    my $q = "INSERT INTO $table (" . join(',',@db_fields) . ") VALUES(NULL" . ',?' x (scalar(@db_fields)-1) . ')';
+#$sth = $dbh->prepare("INSERT INTO $table VALUES(NULL,?,?,?,?,?,?,?,?,?,?,?,?)");
+    $sth = $dbh->prepare($q);
+    };
+if ($EVAL_ERROR) {
+    die "Error while preparing sql statement: $EVAL_ERROR";
+}
+
+sub parse($) {
+    my ($line) = @_;
+    my (@t) = $line =~ /^L(\d+\.\d+) *(\d+?) (.*?) (.*?)\/(\d+?) (\d+?) (.*?) (.*?) (.*?) (.*?)\/(.*?) (.*)$/;
+}
+
+# main loop
+while (my $line = <>) {
+    chomp $line;
+
+    my $cmd = substr($line, 0, 1);      # extract command byte
+
+    if ( $cmd eq 'L' ) {
+        my @log_entry = parse($line);
+        eval {                  # we catch db errors to avoid crashing squid in case something goes wrong...
+            $sth->execute(@log_entry) or die $sth->errstr
+            };
+        if ( $EVAL_ERROR ) {    # leave a trace of the error in the logs
+            warn $EVAL_ERROR . " values=(" . join(', ', @log_entry) . ')';
+        }
+    }
+}
+
+$dbh->disconnect();
+
+__END__