package LIMS::Local::QueryLog;
use strict;
use Data::Dumper;
use Readonly;
Readonly::Array my @STATEMENTS
=> ( qw/SELECT INSERT UPDATE DELETE SHOW/, 'Making method' );
Readonly::Array my @ENV_OK => qw(DEVEL_SERVER RDBO_DEBUG_ON);
our $SKIP_LOG_QUERY; # allow specific queries to be exempted from log files (eg user msgs)
$|++;
BEGIN {
use vars qw($LogArgs $cfg $dispatcher $path_to_app); # to make available to _warn()
use POSIX qw(strftime);
use Log::Dispatch;
use Log::Dispatch::File;
use Log::Dispatch::File::Rolling;
# use Log::Dispatch::FileRotate; # huge performance penalty
use LIMS::Local::Config;
use Data::Dumper;
use IO::All;
use Sub::Exporter -setup =>
{ exports => [ qw(set_querylog_args clear_querylog_args) ] };
$cfg = LIMS::Local::Config->instance;
$path_to_app = $cfg->{path_to_app_root};
$dispatcher = Log::Dispatch->new;
my $sql_file; # set inside blocks below:
# UPDATE/INSERT/DELETE statements (production database; rolling logfile):
if ($ENV{ROSEDB_DEVINIT} =~ /devinit_prod/) {
$dispatcher->add(
Log::Dispatch::File::Rolling->new(
name => 'edits',
min_level => 'info',
# Log::Dispatch::File::Rolling:
filename => $path_to_app . '/logs/%d{yyyy_MMdd}_sql.log',
permissions => 0666,
mode => 'append',
)
);
$sql_file = 'sql.txt';
}
# UPDATE/INSERT/DELETE statements (devel database; fixed logfile):
else {
$dispatcher->add(
Log::Dispatch::File->new(
name => 'edits',
min_level => 'info',
# Log::Dispatch::File:
filename => $path_to_app . '/logs/dev_sql.log',
mode => 'append',
)
);
$sql_file = 'dev_sql.txt';
}
# SELECT queries:
$dispatcher->add(
Log::Dispatch::File->new(
name => 'queries',
min_level => 'debug',
# Log::Dispatch::File:
filename => $path_to_app . '/logs/' . $sql_file,
permissions => 0666,
mode => 'append',
)
);
$SIG{__WARN__} = \&_warn_handler;
}
sub set_querylog_args {
$LogArgs = shift;
# $LogArgs{username} = $username;
}
sub clear_log_args {
# %LogArgs = ();
}
# need to call this from app per-request (eg in cgiapp_prerun) if using fastcgi
# in non-external mode - CGI::Fast calls FCGI which re-defines $SIG{__WARN__};
# doesn't cause the same problem when app run as FastCgiExternalServer process
sub ensure_warn_handler_capture { $SIG{__WARN__} = \&_warn_handler; }
sub _warn_handler {
my $warn = shift || return; # warn $SKIP_LOG_QUERY;
my $time_now = LIMS::Local::Utils::date_and_time_now;
# my $divider = '-+-' x 10;
# $warn =~ s/\s{2}(JOIN)/\n\t$1/g; # line-break & tab JOIN's
my $msg = sub { # reading pid id & size kills print run (timeout):
# sprintf "[%s] %s [%s|%s|%s]\n%s\n", # [time] user [centre, pid ID & size] query:
# $time_now, uc $LogArgs->{user}, uc $cfg->{settings}{_centre},
# $$, format_number(`ps -p $$ -o size=`+0), $warn;
sprintf "[%s] %s [%s]\n%s\n", # [time] user [centre] query:
$time_now, uc $LogArgs->{user}, uc $cfg->{settings}{_centre}, $warn;
}; # CORE::warn(&$msg);
unless ($SKIP_LOG_QUERY) { # warn 'here';
if ($warn =~ /^(INSERT|UPDATE|DELETE)/) { # ie db edits
$dispatcher->log(level => 'info', message => &$msg); # warn 'here';
}
elsif ($warn =~ /^SELECT|SHOW/) { # SELECT|SHOW, etc - ie non-edits
$dispatcher->log(level => 'debug', message => &$msg); # warn 'here';
}
}
# open my $fh, '>>'.'/tmp/env.txt' or die $!;
# while( my ($k,$v) = each %ENV ) {
# print $fh "$k: $v\n";
# }
# don't want RDBO::Manager or SQL query statements in apache's error.log
if ( grep { $warn =~ /\A$_/ } @STATEMENTS ) { # warn Dumper \%ENV;
return 0 unless grep $ENV{$_}, @ENV_OK; # killing print run:
# only want devel server or by specific request:
# CORE::warn($warn) if ( grep $ENV{$_}, @ENV_OK ) and not $SKIP_LOG_QUERY;
}
else { CORE::warn($warn) }
# dump process size info to stdout if devel server or rdbo_debug:
if ( $warn !~ /^Making method/ ) { # && grep $ENV{$_}, @ENV_OK ) {
my $process_size = `ps -p $$ -o size=`+0;
if ( $ENV{FAST_CGI} ) {
my $logfile = $path_to_app . '/logs/processes/' . $$ . '.csv'; # warn $logfile;
my $line = join ',', $time_now, $process_size;
io($logfile)->append($line, "\n");
}
else {
CORE::warn sprintf "* process-size=%s\n", $process_size;
}
}
}
=begin # FileRotate causes serious performance hit here
$dispatcher->add(
Log::Dispatch::FileRotate->new(
name => 'query',
min_level => 'debug',
filename => "$path_to_app_root/logs/sql.txt",
permissions => 0666,
mode => 'append',
DatePattern => 'yyyy-MM-dd', # every day # or size => 1, # operates in date or size mode
)
);
=cut
1;