Which script initialized module?
-
09-01-2021 - |
Pregunta
In Perl, is there any way to tell which .pl script has initialized this instance of a module?
Specifically, I'd like to get the name of the script calling a module, which has a Log4perl
object it. That way, I'll know which .log file I want to write to within the module.
Am I doing this wrong? If I define the $logger
in my .pl script, will any $logger
calls within the module write to the same .log file as the calling script?
I don't have any sample code yet, but have been reading up on Log4perl
. Basically, if I set an Appender to a file, caller.log
, which is the file appender for my calling script, caller.pl
, I'd want any logging defined in the custom imported module, to also write to caller.log
(implicitly, if possible -- obviously I could just pass the name of the log name when I initialize the module instance).
Is this possible without passing arguments specifying which File Appender the module should write to? Doesn't Log4perl
use just one $logger
instance?
Also, let me know if I'm way out, and if there's a different approach I should be considering.
Thank you
EDIT: Sorry, after I posted this, I looked at the Related Links, and I guess my search wording just wasn't correct. It looks like this is a pretty good solution: Self logging Perl modules (without Moose)
If anyone has any other ideas, though, please let me know.
EDIT 2: Finally tested, and got it to work as I had wanted -- was a lot easier than was making it out to be, too!
This is my setup, pretty much:
Module.pm
package Module;
use Log::Log4perl qw(get_logger :levels);
use Data::Dumper;
my $logger = get_logger("Module");
sub new {
my ($class, $name) = @_;
my @caller = caller(0);
$logger->debug("Creating new Module. Called by " . Dumper(\@caller));
my $object = { 'name' => $name };
return bless($object, $class);
}
caller.pl
use Module;
use Log::Log4perl qw(get_logger :levels);
use Data::Dumper;
my $PATH = "$ENV{'APPS'}/$ENV{'OUTDIR'}";
my $SCRIPT = "caller";
my $logger = get_logger("Module");
$logger->level($DEBUG);
my $file_appender = Log::Log4perl::Appender->new("Log::Dispatch::File",
filename=> "$PATH/$SCRIPT.log",
mode => "append",);
$logger->add_appender($file_appender);
my $layout = Log::Log4perl::Layout::PatternLayout->new("%d %p> %F{1}:%L %M - %m%n");
$file_appender->layout($layout);
my $lib = Module->new('Chris');
$logger->info(Dumper($lib));
Solución
You could subclass Log4perl, overriding its constructor. In your custom constructor, use caller()
to get the filename that called the constructor and put it in $self
.
Otros consejos
You can put a subroutine hook into @INC
that can run arbitrary code, as documented in perldoc -f require
. For example:
# UseLogger.pm
package UseLogger;
sub import { unshift @INC, \&UseLogger::log_use }
sub log_use {
my ($self, $filename) = @_;
my @c = caller(0);
print "Module $filename required in file $c[1] line $c[2]\n";
return 0;
}
1;
$ perl -MUseLogger my_script.pl
Module feature.pm required in file my_script.pl line 2
Module Encode.pm required in file my_script.pl line 5
Module XSLoader.pm from /usr/lib/perl5/5.14.0/cygwin-thread-multi-64int/Encode.pm line 13
...
$0
contains the path to the script. You can use File::Basename's basename
if you want to want the file name component.