Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hi Monks,
I have this small Perl Module to trap warning error(s) to a text file, but it sometimes instead of creating like it supposed to a text file into the current directory where the program is running from, it is creating the text file to the root directory of the program calling it. I don't even know where to start looking for this bug, may be there is something on my module that should be done differently n order not to produce surprises like that.
any help or thoughts on that?
Thanks and here is a sample of this module:

package Log::Error; use 5.008004; use strict; use Cwd; use CGI::Carp qw(carpout); use CGI qw(:standard); use CGI::Carp qw(fatalsToBrowser); use vars qw(@EXPORT @ISA $VERSION); require Exporter; @ISA = qw(Exporter); @EXPORT = qw( a_log e_log ); $VERSION = '0.01'; my $localtime = localtime; our $dir; BEGIN { if ($::dir) { $dir = $::dir; } else { $0 =~ '(.*[\\\/])\w+\.\w+$'; $dir = $1; } } # Set up main error log file my $log_file = "e_log.txt"; #Croak will get the line number of the calling program, not the librar +y itself. open(LOG, ">>$dir/$log_file") or croak "Unable to append to error log: + $!"; carpout(*LOG); ##### ***subroutine who print in Appplication Specific Error*** sub a_log{ my $a_localtime = localtime; my ($a_log, @data) = @_; #To determine the name of the currently running function, including it +s package by using the built-in function "caller". my $sub_al = (caller(0))[3]; #Make sure that the filename doesn't have spaces or extensions like ex +e or bat on it, #otherwise it will create file as app_log.txt instead. unless($a_log=~/^[a-zA-Z_]+\.(?!bat$|exe$)[a-zA-Z_]{3}$/gi) { $a_log="a_log.txt" } open(LOGAPP, ">>$dir$a_log") or croak "Unable to append to $a_log: $!"; #print LOGAPP "[$localtime] $a_log : @data ::::: $!\n"; print LOGAPP "[$a_localtime] $a_log : @data \n"; close LOGAPP or die "Cannot close log file:: $a_log : $!\n"; } sub e_log{ my $e_localtime = localtime; my ($e_log, @e_data) = @_; #To determine the name of the currently running function, including it +s package by using the built-in function "caller". my $sub_el = (caller(0))[3]; #Make sure that the filename doesn't have spaces or extensions like ex +e or bat on it, #otherwise it will create file as app_log.txt instead. unless($e_log=~/^[a-zA-Z_]+\.(?!bat$|exe$)[a-zA-Z_]{3}$/gi) { $e_log="errorapp_log.txt" } open(ERRAPPLOG, ">>$dir$e_log") or croak "Unable to append to $e_log: $!"; print ERRAPPLOG "[$e_localtime] $e_log : @e_data\n"; close ERRAPPLOG or die "Cannot close log file:: $e_log : $!\n"; } 1; __END__ =head1 DESCRIPTION Errors trapped by -w (Warning) will issue a warning and report the lin +e number of the error. Die will cause the program to die and report the line number of the er +ror. All the errors will be printed to e_log.txt in the same directory wher +e the program that caused the error is running. Called like: &e_log('my_file.txt','Oh no, $hit hit the fan.');

2006-05-03 Retitled by Arunbear, as per Monastery guidelines
Original title: 'Module Question'

Replies are listed 'Best First'.
Re: Logging module sometimes writes its file in the wrong directory
by davidrw (Prior) on May 03, 2006 at 15:56 UTC
    I don't even know where to start looking for this bug,
    It's opening in the "wrong" place, so let's start with the open call:
    open(LOG, ">>$dir/$log_file") or croak "Unable to append to error log: + $!";
    So it uses $dir .. now to see where that's set, which is in BEGIN .. and has this snippet:
    $0 =~ '(.*[\\\/])\w+\.\w+$'; $dir = $1;
    Several important items here:
    • never use $1 w/o checking that the match actually succeeded .. if you don't, you'll get unexpected values.
      $dir = $1 if $0 =~ m#/.*[\\/])\w+\.\w+$#;
    • $0 is the program being run, so this is setting $dir to that path. Thus why that's where the log file is being opened.
    • Use something like File::Basename to parse the path instead of a regex. For example, if the script is /usr/local/bin/foo (instead of foo.pl) then your regex fails.
      use File::Basename; $dir = dirname($0);
      Do you think that I should just get rid of the
      BEGIN { if ($::dir) { $dir = $::dir; } else { $0 =~ '(.*[\\\/])\w+\.\w+$'; $dir = $1; } }

      And just use
      use File::Basename; my $dir = dirname($0);

      in the module?
        If you want to use the directory where your script is, then yes. You can also use:
        use FindBin qw($Bin); my $dir = $Bin;
        -imran
        Update: if you want to open the file in the current directory that you are in, don't use $dir, just do open with the filename. This is unless a chdir was done.
Re: Logging module sometimes writes its file in the wrong directory
by bart (Canon) on May 04, 2006 at 14:56 UTC
    You have a bizarre idea about what "current directory" means, IMO. It does not mean "the directory the script is in", it is the directory the user is in, as in what directory is used when you type "dir" or "ls". In Perl, you can get that path by using cwd from the module Cwd.

    If you want to always use the directory the script is in, in theory you should be using FindBin, but it is rubbish. Instead, look at tye's mechanism to derive the directory from $0, in FindBin is broken (RE: How do I get the full path to the script executing?). rel2abs can be found in File::Spec as a class method, or in File::Spec::Functions as a function. (Both come with Perl.)

    Using File::Basename, you can then extract the directory the script is in.