ensembl-hive  2.8.1
Logger.pm
Go to the documentation of this file.
1 =head1 LICENSE
2 
3 See the NOTICE file distributed with this work for additional information
4 regarding copyright ownership.
5 
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
9 
10  http://www.apache.org/licenses/LICENSE-2.0
11 
12 Unless required by applicable law or agreed to in writing, software
13 distributed under the License is distributed on an "AS IS" BASIS,
14 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 See the License for the specific language governing permissions and
16 limitations under the License.
17 
18 =cut
19 
20 
21 =head1 CONTACT
22 
23  Please email comments or questions to the public Ensembl
24  developers list at <http://lists.ensembl.org/mailman/listinfo/dev>.
25 
26  Questions may also be sent to the Ensembl help desk at
27  <http://www.ensembl.org/Help/Contact>.
28 
29 =cut
30 
31 =head1 NAME
32 
33 Bio::EnsEMBL::Utils::ConversionSupport - Utility module for Vega release and
34 schema conversion scripts
35 
36 =head1 SYNOPSIS
37 
38  my $serverroot = '/path/to/ensembl';
39  my $suport = new Bio::EnsEMBL::Utils::ConversionSupport($serverroot);
40 
41  # parse common options
42  $support->parse_common_options;
43 
44  # parse extra options for your script
45  $support->parse_extra_options( 'string_opt=s', 'numeric_opt=n' );
46 
47  # ask user if he wants to run script with these parameters
48  $support->confirm_params;
49 
50  # see individual method documentation for more stuff
51 
52 =head1 DESCRIPTION
53 
54 This module is a collection of common methods and provides helper
55 functions for the Vega release and schema conversion scripts. Amongst
56 others, it reads options from a config file, parses commandline options
57 and does logging.
58 
59 =head1 METHODS
60 
61 =cut
62 
63 package Bio::EnsEMBL::Utils::Logger;
64 
65 use strict;
66 use warnings;
67 no warnings 'uninitialized';
68 
69 use FindBin qw($Bin $Script);
70 use POSIX qw(strftime);
71 use Bio::EnsEMBL::Utils::Argument qw(rearrange);
72 use Bio::EnsEMBL::Utils::Exception qw(throw);
73 use Bio::EnsEMBL::Utils::ScriptUtils qw(parse_bytes);
74 use Carp;
75 
76 my %level_defs = (
77  'error' => 1,
78  'warn' => 2,
79  'warning' => 2,
80  'info' => 3,
81  'debug' => 4,
82  'verbose' => 4,
83 );
84 
85 my @reverse_level_defs = (undef, qw(error warning info debug));
86 
87 =head2 new
88 
89  Arg[1] : String $serverroot - root directory of your ensembl sandbox
90  Example : my $support = new Bio::EnsEMBL::Utils::ConversionSupport(
91  '/path/to/ensembl');
92  Description : constructor
93  Return type : Bio::EnsEMBL::Utils::ConversionSupport object
94  Exceptions : thrown on invalid loglevel
95  Caller : general
96 
97 =cut
98 
99 sub new {
100  my $caller = shift;
101  my $class = ref($caller) || $caller;
102 
103  my ($logfile, $logauto, $logautobase, $logautoid, $logpath, $logappend,
104  $loglevel, $is_component) = rearrange(
105  ['LOGFILE', 'LOGAUTO', 'LOGAUTOBASE', 'LOGAUTOID', 'LOGPATH', 'LOGAPPEND',
106  'LOGLEVEL', 'IS_COMPONENT'], @_);
107 
108  my $self = { '_warnings' => 0, };
109  bless ($self, $class);
110 
111  # initialise
112  $self->logfile($logfile);
113  $self->logpath($logpath);
114  $self->logappend($logappend);
115  $self->is_component($is_component);
116 
117  # automatic logfile creation
118  $self->logauto($logauto);
119  $logautoid ||= strftime("%Y%m%d-%H%M%S", localtime);
120  $self->log_auto_id($logautoid);
121  $self->create_auto_logfile($logautobase);
122 
123  $loglevel ||= 'info';
124  if ($loglevel =~ /^\d+$/ and $loglevel > 0 and $loglevel < 5) {
125  $self->{'loglevel'} = $loglevel;
126  } elsif ($level_defs{lc($loglevel)}) {
127  $self->{'loglevel'} = $level_defs{lc($loglevel)};
128  } else {
129  throw('Unknown loglevel: $loglevel.');
130  }
131 
132  return $self;
133 }
134 
135 
136 =head2 log_generic
137 
138  Arg[1] : String $txt - the text to log
139  Arg[2] : Int $indent - indentation level for log message
140  Example : my $log = $support->log_filehandle;
141  $support->log('Log foo.\n', 1);
142  Description : Logs a message to the filehandle initialised by calling
143  $self->log_filehandle(). You can supply an indentation level
144  to get nice hierarchical log messages.
145  Return type : true on success
146  Exceptions : thrown when no filehandle can be obtained
147  Caller : general
148 
149 =cut
150 
151 sub log_generic {
152  my ($self, $txt, $indent, $stamped) = @_;
153 
154  $indent ||= 0;
155  my $fh = $self->log_filehandle;
156 
157  # append timestamp and memory usage to log text if requested
158  if ($stamped) {
159  $txt =~ s/^(\n*)(.*)(\n*)$/$2/;
160  $txt = sprintf("%-60s%20s", $txt, $self->time_and_mem);
161  $txt = $1.$txt.$3;
162  }
163 
164  # strip off leading linebreaks so that indenting doesn't break
165  $txt =~ s/^(\n*)//;
166 
167  # indent
168  $txt = $1." "x$indent . $txt;
169 
170  print $fh "$txt";
171 
172  return(1);
173 }
174 
175 
176 =head2 error
177 
178  Arg[1] : String $txt - the error text to log
179  Arg[2] : Int $indent - indentation level for log message
180  Example : my $log = $support->log_filehandle;
181  $support->log_error('Log foo.\n', 1);
182  Description : Logs a message via $self->log and exits the script.
183  Return type : none
184  Exceptions : none
185  Caller : general
186 
187 =cut
188 
189 sub error {
190  my ($self, $txt, $indent, $stamped) = @_;
191 
192  return(0) unless ($self->{'loglevel'} >= 1);
193 
194  $txt = "ERROR: ".$txt;
195  $self->log_generic($txt, $indent, $stamped);
196 
197  $self->log_generic("\nExiting prematurely.\n\n");
198  $self->log_generic("Runtime: ".$self->runtime." ".$self->date_and_mem."\n\n");
199 
200  confess($txt);
201 }
202 
203 
204 =head2 warning
205 
206  Arg[1] : String $txt - the warning text to log
207  Arg[2] : Int $indent - indentation level for log message
208  Example : my $log = $support->log_filehandle;
209  $support->log_warning('Log foo.\n', 1);
210  Description : Logs a message via $self->log and increases the warning counter.
211  Return type : true on success
212  Exceptions : none
213  Caller : general
214 
215 =cut
216 
217 sub warning {
218  my ($self, $txt, $indent, $stamped) = @_;
219 
220  return(0) unless ($self->{'loglevel'} >= 2);
221 
222  $txt = "WARNING: " . $txt;
223  $self->log_generic($txt, $indent, $stamped);
224 
225  $self->{'_warnings'}++;
226 
227  return(1);
228 }
229 
230 
231 sub info {
232  my ($self, $txt, $indent, $stamped) = @_;
233 
234  return(0) unless ($self->{'loglevel'} >= 3);
235 
236  $self->log_generic($txt, $indent, $stamped);
237  return(1);
238 }
239 
240 
241 =head2 debug
242 
243  Arg[1] : String $txt - the warning text to log
244  Arg[2] : Int $indent - indentation level for log message
245  Example : my $log = $support->log_filehandle;
246  $support->log_verbose('Log this verbose message.\n', 1);
247  Description : Logs a message via $self->log if --verbose option was used
248  Return type : TRUE on success, FALSE if not verbose
249  Exceptions : none
250  Caller : general
251 
252 =cut
253 
254 sub debug {
255  my ($self, $txt, $indent, $stamped) = @_;
256 
257  return(0) unless ($self->{'loglevel'} >= 4);
258 
259  $self->log_generic($txt, $indent, $stamped);
260  return(1);
261 }
262 
263 
264 sub log_progress {
265  my $self = shift;
266  my $name = shift;
267  my $curr = shift;
268  my $indent = shift;
269 
270  throw("You must provide a name and the current value for your progress bar")
271  unless ($name and $curr);
272 
273  # return if we haven't reached the next increment
274  return if ($curr < int($self->{'_progress'}->{$name}->{'next'}));
275 
276  my $index = $self->{'_progress'}->{$name}->{'index'};
277  my $num_bins = $self->{'_progress'}->{$name}->{'numbins'};
278  my $percent = $index/$num_bins*100;
279 
280  my $log_str;
281  $log_str .= ' 'x$indent if ($index == 0);
282  $log_str .= "\b"x4;
283  $log_str .= sprintf("%3s%%", $percent);
284  $log_str .= "\n" if ($curr == $self->{'_progress'}->{$name}->{'max_val'});
285 
286  $self->info($log_str);
287 
288  # increment counters
289  $self->{'_progress'}->{$name}->{'index'}++;
290  $self->{'_progress'}->{$name}->{'next'} += $self->{'_progress'}->{$name}->{'binsize'};
291 }
292 
293 
294 sub log_progressbar {
295  my $self = shift;
296  my $name = shift;
297  my $curr = shift;
298  my $indent = shift;
299 
300  throw("You must provide a name and the current value for your progress bar")
301  unless ($name and $curr);
302 
303  # return if we haven't reached the next increment
304  return if ($curr < int($self->{'_progress'}->{$name}->{'next'}));
305 
306  my $index = $self->{'_progress'}->{$name}->{'index'};
307  my $num_bins = $self->{'_progress'}->{$name}->{'numbins'};
308  my $percent = $index/$num_bins*100;
309 
310  my $log_str = "\r".(' 'x$indent)."[".('='x$index).(' 'x($num_bins-$index))."] ${percent}\%";
311  $log_str .= "\n" if ($curr == $self->{'_progress'}->{$name}->{'max_val'});
312 
313  $self->info($log_str);
314 
315  # increment counters
316  $self->{'_progress'}->{$name}->{'index'}++;
317  $self->{'_progress'}->{$name}->{'next'} += $self->{'_progress'}->{$name}->{'binsize'};
318 }
319 
320 
321 sub init_progress {
322  my $self = shift;
323  my $max = shift;
324  my $num_bins = shift || 50;
325 
326  throw("You must provide the maximum value for your progress bar")
327  unless (defined($max));
328 
329  # auto-generate a unique name for your progressbar
330  my $name = time . '_' . int(rand(1000));
331 
332  # calculate bin size; we will use 50 bins (2% increments)
333  my $binsize = $max/$num_bins;
334 
335  $self->{'_progress'}->{$name}->{'max_val'} = $max;
336  $self->{'_progress'}->{$name}->{'binsize'} = $binsize;
337  $self->{'_progress'}->{$name}->{'numbins'} = $num_bins;
338  $self->{'_progress'}->{$name}->{'next'} = 0;
339  $self->{'_progress'}->{$name}->{'index'} = 0;
340 
341  return $name;
342 }
343 
344 
345 =head2 log_filehandle
346 
347  Arg[1] : (optional) String $mode - file access mode
348  Example : my $log = $support->log_filehandle;
349  # print to the filehandle
350  print $log 'Lets start logging...\n';
351  # log via the wrapper $self->log()
352  $support->log('Another log message.\n');
353  Description : Returns a filehandle for logging (STDERR by default, logfile if
354  set from config or commandline). You can use the filehandle
355  directly to print to, or use the smart wrapper $self->log().
356  Logging mode (truncate or append) can be set by passing the
357  mode as an argument to log_filehandle(), or with the
358  --logappend commandline option (default: truncate)
359  Return type : Filehandle - the filehandle to log to
360  Exceptions : thrown if logfile can't be opened
361  Caller : general
362 
363 =cut
364 
365 sub log_filehandle {
366  my ($self, $mode) = @_;
367 
368  unless ($self->{'_log_filehandle'}) {
369  $mode ||= '>';
370  $mode = '>>' if ($self->logappend);
371 
372  my $fh = \*STDERR;
373 
374  if (my $logfile = $self->logfile) {
375  if (my $logpath = $self->logpath) {
376  unless (-e $logpath) {
377  system("mkdir -p $logpath") == 0 or
378  throw("Can't create log dir $logpath: $!\n");
379  }
380 
381  $logfile = "$logpath/".$self->logfile;
382  }
383 
384  open($fh, "$mode", $logfile) or
385  throw("Unable to open $logfile for writing: $!");
386  }
387 
388  $self->{'_log_filehandle'} = $fh;
389  }
390 
391  return $self->{'_log_filehandle'};
392 }
393 
394 
395 =head2 extract_log_identifier
396 
397  Arg[1] :
398  Example :
399  Description :
400  Return type :
401  Exceptions :
402  Caller :
403  Status :
404 
405 =cut
406 
407 sub extract_log_identifier {
408  my $self = shift;
409 
410  if (my $logfile = $self->logfile) {
411  $logfile =~ /.+\.([^\.]+)\.log/;
412  return $1;
413  } else {
414  return undef;
415  }
416 }
417 
418 
419 =head2 init_log
420 
421  Example : $support->init_log;
422  Description : Opens a filehandle to the logfile and prints some header
423  information to this file. This includes script name, date, user
424  running the script and parameters the script will be running
425  with.
426  Return type : Filehandle - the log filehandle
427  Exceptions : none
428  Caller : general
429 
430 =cut
431 
432 sub init_log {
433  my $self = shift;
434  my $params = shift;
435 
436  # get a log filehandle
437  my $log = $self->log_filehandle;
438 
439  # remember start time
440  $self->{'_start_time'} = time;
441 
442  # don't log parameters if this script is run by another one
443  if ($self->logauto or ! $self->is_component) {
444  # print script name, date, user who is running it
445  my $hostname = `hostname`;
446  chomp $hostname;
447  my $script = "$hostname:$Bin/$Script";
448  my $user = `whoami`;
449  chomp $user;
450  $self->info("Script: $script\nDate: ".$self->date."\nUser: $user\n");
451 
452  # print parameters the script is running with
453  if ($params) {
454  $self->info("Parameters:\n\n");
455  $self->info($params);
456  }
457  }
458 
459  return $log;
460 }
461 
462 
463 =head2 finish_log
464 
465  Example : $support->finish_log;
466  Description : Writes footer information to a logfile. This includes the
467  number of logged warnings, timestamp and memory footprint.
468  Return type : TRUE on success
469  Exceptions : none
470  Caller : general
471 
472 =cut
473 
474 sub finish_log {
475  my $self = shift;
476 
477  $self->info("\nAll done for $Script.\n");
478  $self->info($self->warning_count." warnings. ");
479  $self->info("Runtime: ".$self->runtime." ".$self->date_and_mem."\n\n");
480 
481  return(1);
482 }
483 
484 
485 sub runtime {
486  my $self = shift;
487 
488  my $runtime = "n/a";
489 
490  if ($self->{'_start_time'}) {
491  my $diff = time - $self->{'_start_time'};
492  my $sec = $diff % 60;
493  $diff = ($diff - $sec) / 60;
494  my $min = $diff % 60;
495  my $hours = ($diff - $min) / 60;
496 
497  $runtime = "${hours}h ${min}min ${sec}sec";
498  }
499 
500  return $runtime;
501 }
502 
503 
504 =head2 date_and_mem
505 
506  Example : print LOG "Time, memory usage: ".$support->date_and_mem."\n";
507  Description : Prints a timestamp and the memory usage of your script.
508  Return type : String - timestamp and memory usage
509  Exceptions : none
510  Caller : general
511 
512 =cut
513 
514 sub date_and_mem {
515  my $date = strftime "%Y-%m-%d %T", localtime;
516  my $mem = `ps -p $$ -o vsz |tail -1`;
517  chomp $mem;
518  $mem = parse_bytes($mem*1000);
519  return "[$date, mem $mem]";
520 }
521 
522 
523 sub time_and_mem {
524  my $date = strftime "%T", localtime;
525  my $mem = `ps -p $$ -o vsz |tail -1`;
526  chomp $mem;
527  $mem = parse_bytes($mem*1000);
528  $mem =~ s/ //;
529  return "[$date|$mem]";
530 }
531 
532 
533 =head2 date
534 
535  Example : print "Date: " . $support->date . "\n";
536  Description : Prints a nicely formatted timestamp (YYYY-DD-MM hh:mm:ss)
537  Return type : String - the timestamp
538  Exceptions : none
539  Caller : general
540 
541 =cut
542 
543 sub date {
544  return strftime "%Y-%m-%d %T", localtime;
545 }
546 
547 
548 =head2 mem
549 
550  Example : print "Memory usage: " . $support->mem . "\n";
551  Description : Prints the memory used by your script. Not sure about platform
552  dependence of this call ...
553  Return type : String - memory usage
554  Exceptions : none
555  Caller : general
556 
557 =cut
558 
559 sub mem {
560  my $mem = `ps -p $$ -o vsz |tail -1`;
561  chomp $mem;
562  return $mem;
563 }
564 
565 
566 =head2 warning_count
567 
568  Example : print LOG "There were ".$support->warnings." warnings.\n";
569  Description : Returns the number of warnings encountered while running the
570  script (the warning counter is increased by $self->log_warning).
571  Return type : Int - number of warnings
572  Exceptions : none
573  Caller : general
574 
575 =cut
576 
577 sub warning_count {
578  my $self = shift;
579  return $self->{'_warnings'};
580 }
581 
582 
583 =head2 logfile
584 
585  Arg[1] :
586  Example :
587  Description : Getter and setter for the logfile
588  Return type :
589  Exceptions :
590  Caller :
591  Status :
592 
593 =cut
594 
595 sub logfile {
596  my $self = shift;
597  $self->{'_logfile'} = shift if (@_);
598  return $self->{'_logfile'};
599 }
600 
601 
602 =head2 log_auto_id
603 
604  Arg[1] :
605  Example :
606  Description :
607  Return type :
608  Exceptions :
609  Caller :
610  Status :
611 
612 =cut
613 
614 sub log_auto_id {
615  my $self = shift;
616  $self->{'_log_auto_id'} = shift if (@_);
617  return $self->{'_log_auto_id'};
618 }
619 
620 
621 sub logauto {
622  my $self = shift;
623  $self->{'_log_auto'} = shift if (@_);
624  return $self->{'_log_auto'};
625 }
626 
627 
628 =head2 create_auto_logfile
629 
630  Arg[1] :
631  Example :
632  Description :
633  Return type :
634  Exceptions :
635  Caller :
636  Status : At Risk
637  : under development
638 
639 =cut
640 
641 sub create_auto_logfile {
642  my $self = shift;
643  my $logautobase = shift;
644 
645  # do nothing if automatic logfile generation isn't set
646  return unless ($self->logauto);
647 
648  # an explicit logfile name overrides LOGAUTO
649  return if ($self->logfile);
650 
651  # argument check
652  unless ($logautobase) {
653  throw('Need a base logfile name for auto-generating logfile.');
654  }
655 
656  # create a logfile name
657  $self->logfile("${logautobase}_".$self->log_auto_id.".log");
658 }
659 
660 
661 =head2 logpath
662 
663  Arg[1] :
664  Example :
665  Description :
666  Return type :
667  Exceptions :
668  Caller :
669  Status :
670 
671 =cut
672 
673 sub logpath {
674  my $self = shift;
675  $self->{'_logpath'} = shift if (@_);
676  return $self->{'_logpath'};
677 }
678 
679 
680 =head2 logappend
681 
682  Arg[1] :
683  Example :
684  Description :
685  Return type :
686  Exceptions :
687  Caller :
688  Status :
689 
690 =cut
691 
692 sub logappend {
693  my $self = shift;
694  $self->{'_logappend'} = shift if (@_);
695  return $self->{'_logappend'};
696 }
697 
698 
699 =head2 is_component
700 
701  Arg[1] :
702  Example :
703  Description :
704  Return type :
705  Exceptions :
706  Caller :
707  Status :
708 
709 =cut
710 
711 sub is_component {
712  my $self = shift;
713  $self->{'_is_component'} = shift if (@_);
714  return $self->{'_is_component'};
715 }
716 
717 
718 sub loglevel {
719  my $self = shift;
720  return $reverse_level_defs[$self->{'loglevel'}];
721 }
722 
723 
724 #
725 # deprecated methods (left here for backwards compatibility
726 #
727 sub log_error {
728  return $_[0]->error(@_);
729 }
730 
731 sub log_warning {
732  return $_[0]->warning(@_);
733 }
734 
735 sub log {
736  return $_[0]->info(@_);
737 }
738 
739 sub log_verbose {
740  return $_[0]->debug(@_);
741 }
742 
743 sub log_stamped {
744  return $_[0]->log(@_, 1);
745 }
746 
747 
748 
749 1;
750 
usage
public usage()
Bio::EnsEMBL::Utils::ConversionSupport
Definition: ConversionSupport.pm:39
Bio::EnsEMBL::Utils::ScriptUtils
Definition: ScriptUtils.pm:11
debug
public debug()
Script
Definition: dump_mysql.pl:9
info
public info()
Bio::EnsEMBL::Utils::Argument
Definition: Argument.pm:34
Bio::EnsEMBL::Utils::Exception
Definition: Exception.pm:68