Toggle between hot filehandles question

J

Jorge

Using Perl v5.8.3 built for sun4-solaris-64

I'm putting together a handshaking front-end for a script that will
read a spreadsheet (OLE) and use the results as DBI/DBD directly into
an Oracle table.

Because this will be (someday:)) a production script, I need to keep a
solid record of all the warnings, errors and such in a log as well as
splash that same information to the terminal for the user.

Therefore, I have 2 filehandles that I want to toggle between hot and
cold -- STDOUT and LOG ($fh in subroutine).

A snippet (very pruned down) is below to show how I am doing the
toggling. As already said, it is working but I can't believe it's the
correct way -- it's downright ugly.

Is there a more elegant approach to this.

FWIW: I've been through the FAQ's, the 'suffering from buffering'
article and many others and couldn't find any reference to this
particular situation.

All help is greatly appreciated.

Jorge


#!/usr/local/bin/perl -w

use strict;
use Cwd;

my $log = "log";

my $log_date_time = scalar(localtime);
local $0 = get_pathname_leaf($0);
my $curWorkDir = getcwd();
my $user_name = $ENV{'USER'};

open LOG, ">$log" or die "cannot open $log $!\n";

# make filehandle hot print header lines to log
select( ( select(LOG), $| = 1 )[0] );

print LOG " Created by user ".$user_name." [".$log_date_time."]\n";
print LOG " ==================================================\n";

if(&check_args($#ARGV + 1, \@ARGV, \*LOG) == 1){
select( ( select(LOG), $| = 0 )[0] );
print "Exiting due to errors ... [from calling script]\n";
select( ( select(LOG), $| = 1 )[0] );
print LOG "Exiting due to errors ... [from calling script]\n";
close(LOG);
exit();
}

sub check_args(){
my $numargs = shift;
my $files = shift;
my $fh = shift;

if($numargs != 1){
select( ( select($fh), $| = 0 )[0] );
print "\n\nUsage: $0 input_file <Enter>\n\n";
select( ( select($fh), $| = 1 )[0] );
print $fh "\n\nUsage: $0 input_file <Enter>\n\n";
exit();
}

my $flag = 0;
my $arg;
my $i = 0;

foreach $arg (@$files) {
++$i;
if($i == 1 && (!( -f $arg))){
select( ( select($fh), $| = 0 )[0] );
print "\n\n",$arg," not found \n";
select( ( select($fh), $| = 1 )[0] );
print $fh "\n\n",$arg," not found \n";
$flag = 1;
last;
}
elsif($i == 1 && (!( -s $arg))){
select( ( select($fh), $| = 0 )[0] );
print "\n\n",$arg," is an empty file \n";
select( ( select($fh), $| = 1 )[0] );
print $fh "\n\n",$arg," is an empty file \n";
$flag = 1;
last;
}
elsif($i == 1 && (!( -r $arg))){
select( ( select($fh), $| = 0 )[0] );
print "\n\n",$arg," is not a readable file \n";
select( ( select($fh), $| = 1 )[0] );
print $fh "\n\n",$arg," is not a readable file \n";
$flag = 1;
last;
}
elsif($arg !~ /\.xls$/i){
select( ( select($fh), $| = 0 )[0] );
print "\n\n",$arg," does not appear to be an Excel file\n";
select( ( select($fh), $| = 1 )[0] );
print $fh "\n\n",$arg," does not appear to be an Excel file\n";
$flag = 1;
last;
}
}
if($flag == 1){return 1;}else{return 0;
}
}

sub get_pathname_leaf{
my ($pathname) = @_ ;
my @split_pathname = split( /[\\\/]/, $pathname ) ;
my $leaf = pop( @split_pathname ) ;
return( $leaf ) ;
}
 
U

usenet

Because this will be (someday:)) a production script, I need to keep a
solid record of all the warnings, errors and such in a log as well as
splash that same information to the terminal for the user.

Therefore, I have 2 filehandles that I want to toggle between hot and
cold -- STDOUT and LOG ($fh in subroutine).

You can use IO::Tee to multiplex the output to a file as well as the
terminal.

However, you may wish to consider a full-blown logging module. I like
Log::Dispatch. You do a lot of work at the beginning of your program
(but I use mostly boilerplate code) to set up the various logging
methods (screen, file, database, e-mail, etc) but then it's really
easy to invoke one or more of those methods at any time (depending on
the severity level of the event).

Consider this boilerplate which sets up three log methods (screen,
file, and e-mail):

######################################################################
use Log::Dispatch; ##
use Log::Dispatch::Screen; ##
use Log::Dispatch::File; ##
use Log::Dispatch::Email::MailSendmail; ##
##
my $log; ##
##
my $logdir = "/var/tmp"; ##
my @admin_email = ('(e-mail address removed)') ##
##
my $add_lf = sub { my %p = @_; "$p{'message'}\n"}; ##
my $add_indent = sub {my %p = @_; " $p{'message'}"}; #for Outlook ##
my $add_timestamp = sub { my %p = @_; ##
sprintf "%s - %s", scalar(localtime), ##
$p{'message'}; }; ##
my $add_level = sub { my %p = @_; ##
sprintf "%-10s %s", ($p{'level'} =~ /debug/i) ##
? lc $p{'level'} ##
: uc $p{'level'},##
$p{'message'} }; ##
##
$log = Log::Dispatch->new ( callbacks => [$add_level, $add_lf] ); ##
##
$log ->add( Log::Dispatch::Screen ->new( ##
name => 'screen', ##
min_level => 'debug', ##
stderr => 0, ) ##
); ##
$log ->add( Log::Dispatch::File ->new( ##
name => 'file', ##
min_level => 'info', ##
filename => sprintf ( "%s/%s.log", ##
$logdir, ##
$FindBin::Script ), ##
mode => 'append', ##
callbacks => $add_timestamp, ##
)); ##
$log ->add( Log::Dispatch::Email::MailSendmail ->new( ##
name => 'email', ##
min_level => 'error', ##
to => \@admin_email, ##
subject => "ERROR in $PROGRAM_NAME", ##
callbacks => $add_indent, ##
from => sprintf ("SERVER<%s\@%s>", ##
(hostname =~ /^([^\.]*)/)[0], ##
'do-not-reply.com' ) , ##
smtp => $cfg{'email'}{'smtp'} || 'localhost', ##
)); ##
######################################################################

Now all you need to do is log an event at an appropriate error level,
such as:

$log->debug("Opening $file"); #screen only
$log->info("Added user $user") #screen + logfile
$log->error("I just jumped the shark!"); #and also sends email

You can create a method to log to a database as well, and you can use
a file logging variant that automatically rotates logfiles if you
like. I use callbacks to add newlines, and I use a callback to
timestamp log file entries.

You could set up multiple file logging methods which fire at different
severity levels, so you could keep a debug-level logfile (which you
would probably rotate more frequently) and a historical main-event
logfile (which you would rotate infrequently or never).
 
J

Jorge

Because this will be (someday:)) a production script, I need to keep a
solid record of all the warnings, errors and such in a log as well as
splash that same information to the terminal for the user.
Therefore, I have 2 filehandles that I want to toggle between hot and
cold -- STDOUT and LOG ($fh in subroutine).

You can use IO::Tee to multiplex the output to a file as well as the
terminal.

However, you may wish to consider a full-blown logging module. I like
Log::Dispatch. You do a lot of work at the beginning of your program
(but I use mostly boilerplate code) to set up the various logging
methods (screen, file, database, e-mail, etc) but then it's really
easy to invoke one or more of those methods at any time (depending on
the severity level of the event).

Consider this boilerplate which sets up three log methods (screen,
file, and e-mail):

######################################################################
use Log::Dispatch; ##
use Log::Dispatch::Screen; ##
use Log::Dispatch::File; ##
use Log::Dispatch::Email::MailSendmail; ##
##
my $log; ##
##
my $logdir = "/var/tmp"; ##
my @admin_email = ('(e-mail address removed)') ##
##
my $add_lf = sub { my %p = @_; "$p{'message'}\n"}; ##
my $add_indent = sub {my %p = @_; " $p{'message'}"}; #for Outlook ##
my $add_timestamp = sub { my %p = @_; ##
sprintf "%s - %s", scalar(localtime), ##
$p{'message'}; }; ##
my $add_level = sub { my %p = @_; ##
sprintf "%-10s %s", ($p{'level'} =~ /debug/i) ##
? lc $p{'level'} ##
: uc $p{'level'},##
$p{'message'} }; ##
##
$log = Log::Dispatch->new ( callbacks => [$add_level, $add_lf] ); ##
##
$log ->add( Log::Dispatch::Screen ->new( ##
name => 'screen', ##
min_level => 'debug', ##
stderr => 0, ) ##
); ##
$log ->add( Log::Dispatch::File ->new( ##
name => 'file', ##
min_level => 'info', ##
filename => sprintf ( "%s/%s.log", ##
$logdir, ##
$FindBin::Script ), ##
mode => 'append', ##
callbacks => $add_timestamp, ##
)); ##
$log ->add( Log::Dispatch::Email::MailSendmail ->new( ##
name => 'email', ##
min_level => 'error', ##
to => \@admin_email, ##
subject => "ERROR in $PROGRAM_NAME", ##
callbacks => $add_indent, ##
from => sprintf ("SERVER<%s\@%s>", ##
(hostname =~ /^([^\.]*)/)[0], ##
'do-not-reply.com' ) , ##
smtp => $cfg{'email'}{'smtp'} || 'localhost', ##
)); ##
######################################################################

Now all you need to do is log an event at an appropriate error level,
such as:

$log->debug("Opening $file"); #screen only
$log->info("Added user $user") #screen + logfile
$log->error("I just jumped the shark!"); #and also sends email

You can create a method to log to a database as well, and you can use
a file logging variant that automatically rotates logfiles if you
like. I use callbacks to add newlines, and I use a callback to
timestamp log file entries.

You could set up multiple file logging methods which fire at different
severity levels, so you could keep a debug-level logfile (which you
would probably rotate more frequently) and a historical main-event
logfile (which you would rotate infrequently or never).

David

Thank you very much for the response. This looks like precisely what I
need. Not only is it nearly immediately functional but it appears I
can build on it as the needs arise.

Thanks again

Jorge
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,566
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top