Mercurial > repos > mahtabm > ensembl
diff variant_effect_predictor/Bio/EnsEMBL/Utils/Logger.pm @ 0:1f6dce3d34e0
Uploaded
author | mahtabm |
---|---|
date | Thu, 11 Apr 2013 02:01:53 -0400 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/variant_effect_predictor/Bio/EnsEMBL/Utils/Logger.pm Thu Apr 11 02:01:53 2013 -0400 @@ -0,0 +1,739 @@ +=head1 LICENSE + + Copyright (c) 1999-2012 The European Bioinformatics Institute and + Genome Research Limited. All rights reserved. + + This software is distributed under a modified Apache license. + For license details, please see + + http://www.ensembl.org/info/about/code_licence.html + +=head1 CONTACT + + Please email comments or questions to the public Ensembl + developers list at <dev@ensembl.org>. + + Questions may also be sent to the Ensembl help desk at + <helpdesk@ensembl.org>. + +=cut + +=head1 NAME + +Bio::EnsEMBL::Utils::ConversionSupport - Utility module for Vega release and +schema conversion scripts + +=head1 SYNOPSIS + + my $serverroot = '/path/to/ensembl'; + my $suport = new Bio::EnsEMBL::Utils::ConversionSupport($serverroot); + + # parse common options + $support->parse_common_options; + + # parse extra options for your script + $support->parse_extra_options( 'string_opt=s', 'numeric_opt=n' ); + + # ask user if he wants to run script with these parameters + $support->confirm_params; + + # see individual method documentation for more stuff + +=head1 DESCRIPTION + +This module is a collection of common methods and provides helper +functions for the Vega release and schema conversion scripts. Amongst +others, it reads options from a config file, parses commandline options +and does logging. + +=head1 METHODS + +=cut + +package Bio::EnsEMBL::Utils::Logger; + +use strict; +use warnings; +no warnings 'uninitialized'; + +use FindBin qw($Bin $Script); +use POSIX qw(strftime); +use Bio::EnsEMBL::Utils::Argument qw(rearrange); +use Bio::EnsEMBL::Utils::Exception qw(throw); +use Bio::EnsEMBL::Utils::ScriptUtils qw(parse_bytes); + +my %level_defs = ( + 'error' => 1, + 'warn' => 2, + 'warning' => 2, + 'info' => 3, + 'debug' => 4, + 'verbose' => 4, +); + +my @reverse_level_defs = (undef, qw(error warning info debug)); + +=head2 new + + Arg[1] : String $serverroot - root directory of your ensembl sandbox + Example : my $support = new Bio::EnsEMBL::Utils::ConversionSupport( + '/path/to/ensembl'); + Description : constructor + Return type : Bio::EnsEMBL::Utils::ConversionSupport object + Exceptions : thrown on invalid loglevel + Caller : general + +=cut + +sub new { + my $caller = shift; + my $class = ref($caller) || $caller; + + my ($logfile, $logauto, $logautobase, $logautoid, $logpath, $logappend, + $loglevel, $is_component) = rearrange( + ['LOGFILE', 'LOGAUTO', 'LOGAUTOBASE', 'LOGAUTOID', 'LOGPATH', 'LOGAPPEND', + 'LOGLEVEL', 'IS_COMPONENT'], @_); + + my $self = { '_warnings' => 0, }; + bless ($self, $class); + + # initialise + $self->logfile($logfile); + $self->logpath($logpath); + $self->logappend($logappend); + $self->is_component($is_component); + + # automatic logfile creation + $self->logauto($logauto); + $logautoid ||= strftime("%Y%m%d-%H%M%S", localtime); + $self->log_auto_id($logautoid); + $self->create_auto_logfile($logautobase); + + $loglevel ||= 'info'; + if ($loglevel =~ /^\d+$/ and $loglevel > 0 and $loglevel < 5) { + $self->{'loglevel'} = $loglevel; + } elsif ($level_defs{lc($loglevel)}) { + $self->{'loglevel'} = $level_defs{lc($loglevel)}; + } else { + throw('Unknown loglevel: $loglevel.'); + } + + return $self; +} + + +=head2 log_generic + + Arg[1] : String $txt - the text to log + Arg[2] : Int $indent - indentation level for log message + Example : my $log = $support->log_filehandle; + $support->log('Log foo.\n', 1); + Description : Logs a message to the filehandle initialised by calling + $self->log_filehandle(). You can supply an indentation level + to get nice hierarchical log messages. + Return type : true on success + Exceptions : thrown when no filehandle can be obtained + Caller : general + +=cut + +sub log_generic { + my ($self, $txt, $indent, $stamped) = @_; + + $indent ||= 0; + my $fh = $self->log_filehandle; + + # append timestamp and memory usage to log text if requested + if ($stamped) { + $txt =~ s/^(\n*)(.*)(\n*)$/$2/; + $txt = sprintf("%-60s%20s", $txt, $self->time_and_mem); + $txt = $1.$txt.$3; + } + + # strip off leading linebreaks so that indenting doesn't break + $txt =~ s/^(\n*)//; + + # indent + $txt = $1." "x$indent . $txt; + + print $fh "$txt"; + + return(1); +} + + +=head2 error + + Arg[1] : String $txt - the error text to log + Arg[2] : Int $indent - indentation level for log message + Example : my $log = $support->log_filehandle; + $support->log_error('Log foo.\n', 1); + Description : Logs a message via $self->log and exits the script. + Return type : none + Exceptions : none + Caller : general + +=cut + +sub error { + my ($self, $txt, $indent, $stamped) = @_; + + return(0) unless ($self->{'loglevel'} >= 1); + + $txt = "ERROR: ".$txt; + $self->log_generic($txt, $indent, $stamped); + + $self->log_generic("\nExiting prematurely.\n\n"); + $self->log_generic("Runtime: ".$self->runtime." ".$self->date_and_mem."\n\n"); + + exit(1); +} + + +=head2 warning + + Arg[1] : String $txt - the warning text to log + Arg[2] : Int $indent - indentation level for log message + Example : my $log = $support->log_filehandle; + $support->log_warning('Log foo.\n', 1); + Description : Logs a message via $self->log and increases the warning counter. + Return type : true on success + Exceptions : none + Caller : general + +=cut + +sub warning { + my ($self, $txt, $indent, $stamped) = @_; + + return(0) unless ($self->{'loglevel'} >= 2); + + $txt = "WARNING: " . $txt; + $self->log_generic($txt, $indent, $stamped); + + $self->{'_warnings'}++; + + return(1); +} + + +sub info { + my ($self, $txt, $indent, $stamped) = @_; + + return(0) unless ($self->{'loglevel'} >= 3); + + $self->log_generic($txt, $indent, $stamped); + return(1); +} + + +=head2 debug + + Arg[1] : String $txt - the warning text to log + Arg[2] : Int $indent - indentation level for log message + Example : my $log = $support->log_filehandle; + $support->log_verbose('Log this verbose message.\n', 1); + Description : Logs a message via $self->log if --verbose option was used + Return type : TRUE on success, FALSE if not verbose + Exceptions : none + Caller : general + +=cut + +sub debug { + my ($self, $txt, $indent, $stamped) = @_; + + return(0) unless ($self->{'loglevel'} >= 4); + + $self->log_generic($txt, $indent, $stamped); + return(1); +} + + +sub log_progress { + my $self = shift; + my $name = shift; + my $curr = shift; + my $indent = shift; + + throw("You must provide a name and the current value for your progress bar") + unless ($name and $curr); + + # return if we haven't reached the next increment + return if ($curr < int($self->{'_progress'}->{$name}->{'next'})); + + my $index = $self->{'_progress'}->{$name}->{'index'}; + my $num_bins = $self->{'_progress'}->{$name}->{'numbins'}; + my $percent = $index/$num_bins*100; + + my $log_str; + $log_str .= ' 'x$indent if ($index == 0); + $log_str .= "\b"x4; + $log_str .= sprintf("%3s%%", $percent); + $log_str .= "\n" if ($curr == $self->{'_progress'}->{$name}->{'max_val'}); + + $self->info($log_str); + + # increment counters + $self->{'_progress'}->{$name}->{'index'}++; + $self->{'_progress'}->{$name}->{'next'} += $self->{'_progress'}->{$name}->{'binsize'}; +} + + +sub log_progressbar { + my $self = shift; + my $name = shift; + my $curr = shift; + my $indent = shift; + + throw("You must provide a name and the current value for your progress bar") + unless ($name and $curr); + + # return if we haven't reached the next increment + return if ($curr < int($self->{'_progress'}->{$name}->{'next'})); + + my $index = $self->{'_progress'}->{$name}->{'index'}; + my $num_bins = $self->{'_progress'}->{$name}->{'numbins'}; + my $percent = $index/$num_bins*100; + + my $log_str = "\r".(' 'x$indent)."[".('='x$index).(' 'x($num_bins-$index))."] ${percent}\%"; + $log_str .= "\n" if ($curr == $self->{'_progress'}->{$name}->{'max_val'}); + + $self->info($log_str); + + # increment counters + $self->{'_progress'}->{$name}->{'index'}++; + $self->{'_progress'}->{$name}->{'next'} += $self->{'_progress'}->{$name}->{'binsize'}; +} + + +sub init_progress { + my $self = shift; + my $max = shift; + my $num_bins = shift || 50; + + throw("You must provide the maximum value for your progress bar") + unless (defined($max)); + + # auto-generate a unique name for your progressbar + my $name = time . '_' . int(rand(1000)); + + # calculate bin size; we will use 50 bins (2% increments) + my $binsize = $max/$num_bins; + + $self->{'_progress'}->{$name}->{'max_val'} = $max; + $self->{'_progress'}->{$name}->{'binsize'} = $binsize; + $self->{'_progress'}->{$name}->{'numbins'} = $num_bins; + $self->{'_progress'}->{$name}->{'next'} = 0; + $self->{'_progress'}->{$name}->{'index'} = 0; + + return $name; +} + + +=head2 log_filehandle + + Arg[1] : (optional) String $mode - file access mode + Example : my $log = $support->log_filehandle; + # print to the filehandle + print $log 'Lets start logging...\n'; + # log via the wrapper $self->log() + $support->log('Another log message.\n'); + Description : Returns a filehandle for logging (STDERR by default, logfile if + set from config or commandline). You can use the filehandle + directly to print to, or use the smart wrapper $self->log(). + Logging mode (truncate or append) can be set by passing the + mode as an argument to log_filehandle(), or with the + --logappend commandline option (default: truncate) + Return type : Filehandle - the filehandle to log to + Exceptions : thrown if logfile can't be opened + Caller : general + +=cut + +sub log_filehandle { + my ($self, $mode) = @_; + + unless ($self->{'_log_filehandle'}) { + $mode ||= '>'; + $mode = '>>' if ($self->logappend); + + my $fh = \*STDERR; + + if (my $logfile = $self->logfile) { + if (my $logpath = $self->logpath) { + unless (-e $logpath) { + system("mkdir -p $logpath") == 0 or + throw("Can't create log dir $logpath: $!\n"); + } + + $logfile = "$logpath/".$self->logfile; + } + + open($fh, "$mode", $logfile) or + throw("Unable to open $logfile for writing: $!"); + } + + $self->{'_log_filehandle'} = $fh; + } + + return $self->{'_log_filehandle'}; +} + + +=head2 extract_log_identifier + + Arg[1] : + Example : + Description : + Return type : + Exceptions : + Caller : + Status : + +=cut + +sub extract_log_identifier { + my $self = shift; + + if (my $logfile = $self->logfile) { + $logfile =~ /.+\.([^\.]+)\.log/; + return $1; + } else { + return undef; + } +} + + +=head2 init_log + + Example : $support->init_log; + Description : Opens a filehandle to the logfile and prints some header + information to this file. This includes script name, date, user + running the script and parameters the script will be running + with. + Return type : Filehandle - the log filehandle + Exceptions : none + Caller : general + +=cut + +sub init_log { + my $self = shift; + my $params = shift; + + # get a log filehandle + my $log = $self->log_filehandle; + + # remember start time + $self->{'_start_time'} = time; + + # don't log parameters if this script is run by another one + if ($self->logauto or ! $self->is_component) { + # print script name, date, user who is running it + my $hostname = `hostname`; + chomp $hostname; + my $script = "$hostname:$Bin/$Script"; + my $user = `whoami`; + chomp $user; + $self->info("Script: $script\nDate: ".$self->date."\nUser: $user\n"); + + # print parameters the script is running with + if ($params) { + $self->info("Parameters:\n\n"); + $self->info($params); + } + } + + return $log; +} + + +=head2 finish_log + + Example : $support->finish_log; + Description : Writes footer information to a logfile. This includes the + number of logged warnings, timestamp and memory footprint. + Return type : TRUE on success + Exceptions : none + Caller : general + +=cut + +sub finish_log { + my $self = shift; + + $self->info("\nAll done for $Script.\n"); + $self->info($self->warning_count." warnings. "); + $self->info("Runtime: ".$self->runtime." ".$self->date_and_mem."\n\n"); + + return(1); +} + + +sub runtime { + my $self = shift; + + my $runtime = "n/a"; + + if ($self->{'_start_time'}) { + my $diff = time - $self->{'_start_time'}; + my $sec = $diff % 60; + $diff = ($diff - $sec) / 60; + my $min = $diff % 60; + my $hours = ($diff - $min) / 60; + + $runtime = "${hours}h ${min}min ${sec}sec"; + } + + return $runtime; +} + + +=head2 date_and_mem + + Example : print LOG "Time, memory usage: ".$support->date_and_mem."\n"; + Description : Prints a timestamp and the memory usage of your script. + Return type : String - timestamp and memory usage + Exceptions : none + Caller : general + +=cut + +sub date_and_mem { + my $date = strftime "%Y-%m-%d %T", localtime; + my $mem = `ps -p $$ -o vsz |tail -1`; + chomp $mem; + $mem = parse_bytes($mem*1000); + return "[$date, mem $mem]"; +} + + +sub time_and_mem { + my $date = strftime "%T", localtime; + my $mem = `ps -p $$ -o vsz |tail -1`; + chomp $mem; + $mem = parse_bytes($mem*1000); + $mem =~ s/ //; + return "[$date|$mem]"; +} + + +=head2 date + + Example : print "Date: " . $support->date . "\n"; + Description : Prints a nicely formatted timestamp (YYYY-DD-MM hh:mm:ss) + Return type : String - the timestamp + Exceptions : none + Caller : general + +=cut + +sub date { + return strftime "%Y-%m-%d %T", localtime; +} + + +=head2 mem + + Example : print "Memory usage: " . $support->mem . "\n"; + Description : Prints the memory used by your script. Not sure about platform + dependence of this call ... + Return type : String - memory usage + Exceptions : none + Caller : general + +=cut + +sub mem { + my $mem = `ps -p $$ -o vsz |tail -1`; + chomp $mem; + return $mem; +} + + +=head2 warning_count + + Example : print LOG "There were ".$support->warnings." warnings.\n"; + Description : Returns the number of warnings encountered while running the + script (the warning counter is increased by $self->log_warning). + Return type : Int - number of warnings + Exceptions : none + Caller : general + +=cut + +sub warning_count { + my $self = shift; + return $self->{'_warnings'}; +} + + +=head2 logfile + + Arg[1] : + Example : + Description : Getter and setter for the logfile + Return type : + Exceptions : + Caller : + Status : + +=cut + +sub logfile { + my $self = shift; + $self->{'_logfile'} = shift if (@_); + return $self->{'_logfile'}; +} + + +=head2 log_auto_id + + Arg[1] : + Example : + Description : + Return type : + Exceptions : + Caller : + Status : + +=cut + +sub log_auto_id { + my $self = shift; + $self->{'_log_auto_id'} = shift if (@_); + return $self->{'_log_auto_id'}; +} + + +sub logauto { + my $self = shift; + $self->{'_log_auto'} = shift if (@_); + return $self->{'_log_auto'}; +} + + +=head2 create_auto_logfile + + Arg[1] : + Example : + Description : + Return type : + Exceptions : + Caller : + Status : At Risk + : under development + +=cut + +sub create_auto_logfile { + my $self = shift; + my $logautobase = shift; + + # do nothing if automatic logfile generation isn't set + return unless ($self->logauto); + + # an explicit logfile name overrides LOGAUTO + return if ($self->logfile); + + # argument check + unless ($logautobase) { + throw('Need a base logfile name for auto-generating logfile.'); + } + + # create a logfile name + $self->logfile("${logautobase}_".$self->log_auto_id.".log"); +} + + +=head2 logpath + + Arg[1] : + Example : + Description : + Return type : + Exceptions : + Caller : + Status : + +=cut + +sub logpath { + my $self = shift; + $self->{'_logpath'} = shift if (@_); + return $self->{'_logpath'}; +} + + +=head2 logappend + + Arg[1] : + Example : + Description : + Return type : + Exceptions : + Caller : + Status : + +=cut + +sub logappend { + my $self = shift; + $self->{'_logappend'} = shift if (@_); + return $self->{'_logappend'}; +} + + +=head2 is_component + + Arg[1] : + Example : + Description : + Return type : + Exceptions : + Caller : + Status : + +=cut + +sub is_component { + my $self = shift; + $self->{'_is_component'} = shift if (@_); + return $self->{'_is_component'}; +} + + +sub loglevel { + my $self = shift; + return $reverse_level_defs[$self->{'loglevel'}]; +} + + +# +# deprecated methods (left here for backwards compatibility +# +sub log_error { + return $_[0]->error(@_); +} + +sub log_warning { + return $_[0]->warning(@_); +} + +sub log { + return $_[0]->info(@_); +} + +sub log_verbose { + return $_[0]->debug(@_); +} + +sub log_stamped { + return $_[0]->log(@_, 1); +} + + + +1; +