From: Gordon M. <gm...@gm...> - 2003-06-03 14:27:06
|
I've been using Log::Log4perl for several months now, with great success - great job! I've designed my Perl object to contain a reference to a Log::Log4perl object, and this seems to work for all methods in the object except DESTROY. Here's an example of what my DESTROY method looks like (the name of my object and it's package is "BE"): sub DESTROY { my ($self) = @_; my ($logger) = Log::Log4perl->get_logger("BE"); $logger->info("Entering DESTROY method for " . __PACKAGE__ . "\n"); } However, when my script ends, I get the following error message as the DESTROY method of my object gets called: (in cleanup) Can't call method "log" on an undefined value at (eval 271) line 42 during global destruction. Since I'm keeping a reference to the Log::Log4perl object inside my object, I'm curious as to why the Log::Log4perl object is getting destroyed before my object enters the DESTROY method. Just to check, I used Data::Dumper to print the state of the object while entering other methods, and then when entering the DESTROY method - note the difference: Upon entering any normal method: $VAR1 = bless( { '_diskgroup' => 'rootdg', '_logger' => bless( { 'DEBUG' => sub { "DUMMY" }, 'additivity' => 1, 'ALL' => sub { "DUMMY" }, 'appender_names' => [ 'ConsoleAppndr', 'FileAppndr2' ], 'FATAL' => $VAR1->{'_logger'}{'DEBUG'}, 'ERROR' => $VAR1->{'_logger'}{'DEBUG'}, 'WARN' => $VAR1->{'_logger'}{'DEBUG'}, 'INFO' => $VAR1->{'_logger'}{'DEBUG'}, 'level' => 10000, 'layout' => undef, 'category' => 'BE', 'num_appenders' => 14, 'OFF' => $VAR1->{'_logger'}{'DEBUG'} }, 'Log::Log4perl::Logger' ), '_mountpoint' => '/.lbbe.orig', '_bootable' => '1', '_complete' => '1', '_filesystems' => { '/' => [ 'rootvol', 'c0t0d0s0' ] }, '_device' => 'c0t0d0', '_name' => 'orig', '_creation' => undef, '_dgid' => '1038429905.1025.ns3' }, 'BE' ); Upon entering the DESTROY method: $VAR1 = bless( { '_diskgroup' => 'rootdg', '_logger' => undef, '_mountpoint' => '/.lbbe.orig', '_bootable' => '1', '_complete' => '1', '_filesystems' => { '/' => [ 'rootvol', 'c0t0d0s0' ] }, '_device' => 'c0t0d0', '_name' => 'orig', '_creation' => undef, '_dgid' => '1038429905.1025.ns3' }, 'BE' ); Note how _logger is now undef. Any ideas on how to work around this problem? I'd rather not resort to just using print() inside the DESTROY method. -- Gordon Marler <gm...@gm...> |
From: Kevin G. <ke...@go...> - 2003-06-03 15:18:22
|
> sub DESTROY { > my ($self) = @_; > my ($logger) = Log::Log4perl->get_logger("BE"); > $logger->info("Entering DESTROY method for " . __PACKAGE__ . "\n"); > } > > However, when my script ends, I get the following error message as the > DESTROY method of my object gets called: > > (in cleanup) Can't call method "log" on an undefined value at > (eval 271) line 42 during global destruction. > Since I'm keeping a reference to the Log::Log4perl object inside my > object, If that's what you're doing, then use the reference instead of asking the in-the-process-of-being-garbage-collected log4perl structs for one. Try it like this: sub DESTROY { my ($self) = @_; my ($logger) = $self->{_logger}; #using exising reference $logger->info("Entering DESTROY method for " . __PACKAGE__ . "\n"); } -- Happy Trails . . . Kevin M. Goess (and Anne and Frank) 904 Carmel Ave. Albany, CA 94706 (510) 525-5217 |
From: Gordon M. <gm...@gm...> - 2003-06-03 15:24:54
|
I see what you're getting at - unfortunately, the change to using: my ($logger) = $self->{_logger}; still fails, as $self->{_logger} is "undef"! As the Data::Dumper output I included indicates, the Log4perl reference in $self->{_logger} has been garbage collected by Perl before my object's DESTROY method ever gets called. On Tue, 2003-06-03 at 11:18, Kevin Goess wrote: > > sub DESTROY { > > my ($self) = @_; > > my ($logger) = Log::Log4perl->get_logger("BE"); > > $logger->info("Entering DESTROY method for " . __PACKAGE__ . "\n"); > > } > > > > However, when my script ends, I get the following error message as the > > DESTROY method of my object gets called: > > > > (in cleanup) Can't call method "log" on an undefined value at > > (eval 271) line 42 during global destruction. > > > Since I'm keeping a reference to the Log::Log4perl object inside my > > object, > > If that's what you're doing, then use the reference instead of asking > the in-the-process-of-being-garbage-collected log4perl structs for one. > Try it like this: > > sub DESTROY { > my ($self) = @_; > my ($logger) = $self->{_logger}; #using exising reference > $logger->info("Entering DESTROY method for " . __PACKAGE__ . "\n"); > } > > -- Gordon Marler <gm...@gm...> |
From: Kevin G. <ke...@go...> - 2003-06-03 16:12:35
|
Gordon Marler wrote: > I see what you're getting at - unfortunately, the change to using: > > my ($logger) = $self->{_logger}; > > still fails, as $self->{_logger} is "undef"! As the Data::Dumper output > I included indicates, the Log4perl reference in $self->{_logger} has > been garbage collected by Perl before my object's DESTROY method ever > gets called. Doh! You're right, sorry. I tried reconstructing your situation from the subsequent code samples you sent and it seems to go ok: use BE; $be = new BE; undef $be; #logs message from DESTROY just fine print "\nfinished\n"; So there must be something else going on. Nothing immediately occurs to me that might cause the behavior you're seeing, but if I think of something I'll let you know. -- Happy Trails . . . Kevin M. Goess (and Anne and Frank) 904 Carmel Ave. Albany, CA 94706 (510) 525-5217 |
From: Mike S. <log...@pe...> - 2003-06-03 15:18:48
|
On Tue, 3 Jun 2003, Gordon Marler wrote: > I've been using Log::Log4perl for several months now, with great success > - great job! Thanks! :) > I've designed my Perl object to contain a reference to a Log::Log4perl > object, and this seems to work for all methods in the object except > DESTROY. Here's an example of what my DESTROY method looks like (the > name of my object and it's package is "BE"): > > sub DESTROY { > my ($self) = @_; > my ($logger) = Log::Log4perl->get_logger("BE"); > $logger->info("Entering DESTROY method for " . __PACKAGE__ . "\n"); > } Hmm ... I'm not quite sure how you're storing references to Log::Logp4erl objects in your object -- there's really no "Log::Log4perl objects". Typically, you call use Log::Log4perl; Log::Log4perl->init(...); at the beginning of your program and then, within your class code, you use package MyClass; use Log::Log4perl; sub method { my ($logger) = Log::Log4perl->get_logger("BE"); $logger->info("message"); } There's really no "Log::Log4perl objects" -- instead, Log4perl uses a singleton mechanism for its loggers. You didn't submit your class' constructor method, so I can only speculate: the problem might be related to storing this "Log::Log4perl reference". Instead, just initialize Log::Log4perl once at the start of the main (!) program and call the loggers in your custom class like shown above, that should fix the problem. Hope that helps! -- Mike Mike Schilli log...@pe... http://perlmeister.com http://log4perl.sourceforge.net |
From: Gordon M. <gm...@gm...> - 2003-06-03 15:33:33
|
I noticed that the Log4perl was pretty much a singleton pattern a while back, so my object's constructor looks like this: our %BE = ( logger => "", ConfigFile => "/etc/lb_lutab", # List of all BE objects ALL => [ ], ); # tri-natured: function, class method, or object method sub _classobj { my $objclass = shift || __PACKAGE__; my $class = ref($objclass) || $objclass; no strict "refs"; # to convert sym ref to real one return \%$class; } for my $datum (keys %{ _classobj() } ) { # turn off strict refs so that we can register a method # in the symbol table no strict "refs"; *$datum = sub { use strict "refs"; my $self = shift->_classobj(); $self->{$datum} = shift if @_; return $self->{$datum}; } } # Constructor sub new { my ($caller,%arg) = @_; my $caller_is_obj = ref($caller); my $class = $caller_is_obj || $caller; my ($logger); unless (BE->logger) { Log::Log4perl::init_and_watch('/usr/LBBE/bootdiskmanager/bin/log.conf',10); my $logger = Log::Log4perl->get_logger('BE'); BE->logger($logger); } $logger = Log::Log4perl->get_logger('BE'); my $instance = { _name => $arg{name} || # Name of this BE undef, ... Other members of the object ... _logger => undef, }; # Save a copy of BE->logger here so the reference to the logger can survive # for use inside DESTROY $instance->{_logger} = $logger; # Append this to the Class list of all BEs my ($all) = BE->ALL(); push @{$all}, $instance; BE->ALL($all); bless $instance, $class; } Note the following: If this is the first BE object in the system, then we have to explicitly create the logger (singleton) and tuck it away. I also put a reference to it in my object's instance, so all of my objects have individual references to the logger. Thus, the logger should stay around until the very last BE object calls it's DESTROY method. This doesn't seem to be the case, as when the last BE object enters DESTROY, $self->{_logger} has the value "undef". Not good. On Tue, 2003-06-03 at 11:47, Mike Schilli wrote: > On Tue, 3 Jun 2003, Gordon Marler wrote: > > > I've been using Log::Log4perl for several months now, with great success > > - great job! > > Thanks! :) > > > I've designed my Perl object to contain a reference to a Log::Log4perl > > object, and this seems to work for all methods in the object except > > DESTROY. Here's an example of what my DESTROY method looks like (the > > name of my object and it's package is "BE"): > > > > sub DESTROY { > > my ($self) = @_; > > my ($logger) = Log::Log4perl->get_logger("BE"); > > $logger->info("Entering DESTROY method for " . __PACKAGE__ . "\n"); > > } > > Hmm ... I'm not quite sure how you're storing references to Log::Logp4erl > objects in your object -- there's really no "Log::Log4perl objects". > > Typically, you call > > use Log::Log4perl; > Log::Log4perl->init(...); > > at the beginning of your program and then, within your class code, > you use > > package MyClass; > > use Log::Log4perl; > > sub method { > my ($logger) = Log::Log4perl->get_logger("BE"); > $logger->info("message"); > } > > There's really no "Log::Log4perl objects" -- instead, Log4perl uses > a singleton mechanism for its loggers. > > You didn't submit your class' constructor method, so I can only speculate: > the problem might be related to storing this "Log::Log4perl reference". > Instead, just initialize Log::Log4perl once at the start of the main (!) > program and call the loggers in your custom class like shown above, that > should fix the problem. > > Hope that helps! > > -- Mike > > Mike Schilli > log...@pe... > http://perlmeister.com > http://log4perl.sourceforge.net -- Gordon Marler <gm...@gm...> |
From: Mike S. <log...@pe...> - 2003-06-04 04:37:56
|
On Tue, 3 Jun 2003, Gordon Marler wrote: > # tri-natured: function, class method, or object method > sub _classobj { > my $objclass = shift || __PACKAGE__; > my $class = ref($objclass) || $objclass; > no strict "refs"; # to convert sym ref to real one > return \%$class; > } > > for my $datum (keys %{ _classobj() } ) { > # turn off strict refs so that we can register a method > # in the symbol table > no strict "refs"; > *$datum = sub { > use strict "refs"; > my $self = shift->_classobj(); > $self->{$datum} = shift if @_; > return $self->{$datum}; > } > } Ok, the previously posted suggestions should fix the problem described here, but just out of curiosity: The reason for the warning message is very obscure. If you call the test program test.pl, consisting of use BE; $be = new BE; undef $be; print "\nfinished\n"; you'll get (in cleanup) Can't call method "log" on an undefined value at (eval 14) line 37 during global destruction with the BE.pm code previously posted by Gordon and attached at the end of this email. However, if you're using the debugger, and run it like perl -d test.pl DB<1> r you'll just get finished Debugged program terminated. Use q to quit or R to restart, Seems like the perl core gets confused with the reference count somehow. Would be interesting if we can isolate the problem to a couple of lines of code so we could submit it to p5p for further investigation ... -- Mike Mike Schilli log...@pe... http://perlmeister.com http://log4perl.sourceforge.net ############################################################ # Code for BE.pm ############################################################ package BE; use Log::Log4perl qw(:easy); %BE = ( logger => "", ConfigFile => "/etc/lb_lutab", # List of all BE objects ALL => [ ], ); #tri-natured: function, class method, or object method sub _classobj { my $objclass = shift || __PACKAGE__; my $class = ref($objclass) || $objclass; no strict "refs"; # to convert sym ref to real one return \%$class; } for my $datum (keys %{ _classobj() } ) { # turn off strict refs so that we can register a method # in the symbol table no strict "refs"; *$datum = sub { use strict "refs"; my $self = shift->_classobj(); $self->{$datum} = shift if @_; return $self->{$datum}; } } # Constructor sub new { my ($caller,%arg) = @_; my $caller_is_obj = ref($caller); my $class = $caller_is_obj || $caller; my ($logger); unless (BE->logger) { Log::Log4perl->easy_init($DEBUG); my $logger = Log::Log4perl->get_logger('BE'); print "logger is $logger\n"; BE->logger($logger); } $logger = Log::Log4perl->get_logger('BE'); my $instance = { _name => $arg{name} || # Name of this BE undef, #... Other members of the object ... _logger => undef, }; # Save a copy of BE->logger here so the reference to the logger can # for use inside DESTROY $instance->{_logger} = $logger; # Append this to the Class list of all BEs my ($all) = BE->ALL(); push @{$all}, $instance; BE->ALL($all); bless $instance, $class; } sub DESTROY { my ($self) = @_; my ($logger) = Log::Log4perl->get_logger("BE"); $logger->info("Entering DESTROY method for " . __PACKAGE__ . "\n"); } 1; |
From: Jim C. <jc...@di...> - 2003-06-03 16:39:53
|
Gordon Marler wrote: >I've been using Log::Log4perl for several months now, with great success >- great job! > >I've designed my Perl object to contain a reference to a Log::Log4perl >object, and this seems to work for all methods in the object except >DESTROY. Here's an example of what my DESTROY method looks like (the >name of my object and it's package is "BE"): > >sub DESTROY { > my ($self) = @_; > my ($logger) = Log::Log4perl->get_logger("BE"); > $logger->info("Entering DESTROY method for " . __PACKAGE__ . "\n"); >} > > >However, when my script ends, I get the following error message as the >DESTROY method of my object gets called: > > (in cleanup) Can't call method "log" on an undefined value at >(eval 271) line 42 during global destruction. > > > a couple things to consider: change the order of use, so that the END block in question runs before Log::Log4perl is unloaded. is your DESTROY being called 'really late' ? ie as a part of process cleanup rather than a variable going out of scope ? If so, Id look to make it (ie $it->DESTROY) a my var, in a package that is reclaimed early in the process end. FWIW - Im using a logger object from an END block succesfully, in Log::Log4perl::AutoCategorize. I put a beta out just recently :-) You may want to look there. (and send me your perftest/timeit.sh results please) |
From: Mike S. <log...@pe...> - 2003-06-03 17:09:34
|
On Tue, 3 Jun 2003, Gordon Marler wrote: > I noticed that the Log4perl was pretty much a singleton pattern a while > back, so my object's constructor looks like this: > ... > # Constructor > sub new { > ... > unless (BE->logger) { > Log::Log4perl::init_and_watch('/usr/LBBE/bootdiskmanager/bin/log.conf',10); > my $logger = Log::Log4perl->get_logger('BE'); > BE->logger($logger); > } > $logger = Log::Log4perl->get_logger('BE'); > ... > $instance->{_logger} = $logger; I see two problems with this approach: 1) As mentioned in my previous post, Log::Log4perl->init() (or init_and_watch) should only be called in the main program, not in libraries or classes. Reason for this is that there can (currently) only be *one* configuration file. If you call Log::Log4perl->init() multiple times (what you will do implicitely if someone else doesn't comply with this Log4perl mandate either), the last call to init() will overwrite all previous settings. Bottom line: Log::Log4perl->init() (or init_and_watch) should only be called in the main program. 2) my $logger = Log::Log4perl->get_logger('BE'); will always return a reference to the *same* object, no matter how often you call it or which class/object instance you're calling it from. This instance should *never* be stored anywhere or deleted. Always retrieve them via get_logger(). 3) Your particular problem seems to be related with the way your accessors work, I haven't tracked it down yet entirely, but it works with "regular" instance variables. Tonight, I'm gonna look into it again, but it shouldn't be relevant because you can fix the problem by fixing 1) and 2). -- Mike Mike Schilli log...@pe... http://perlmeister.com http://log4perl.sourceforge.net |
From: Gordon M. <gm...@gm...> - 2003-06-03 17:12:58
|
Thanks for the advice - I'll take it to heart and make the recommended change in #1 at the very least. Note that in trying to simplify this issue, I eventually remove the "Class" attributes hash %BE (defined as "our %BE"). I use this to try to keep track of several things related to this application, such as: 1. Total number of BE object instances 2. Config data that is global Seems that if I remove this entirely, the DESTROY problem disappears. I'll look into this more after I implement your suggested changes. Gordon On Tue, 2003-06-03 at 13:25, Mike Schilli wrote: > On Tue, 3 Jun 2003, Gordon Marler wrote: > > > I noticed that the Log4perl was pretty much a singleton pattern a while > > back, so my object's constructor looks like this: > > ... > > # Constructor > > sub new { > > ... > > unless (BE->logger) { > > Log::Log4perl::init_and_watch('/usr/LBBE/bootdiskmanager/bin/log.conf',10); > > my $logger = Log::Log4perl->get_logger('BE'); > > BE->logger($logger); > > } > > $logger = Log::Log4perl->get_logger('BE'); > > ... > > $instance->{_logger} = $logger; > > I see two problems with this approach: > > 1) As mentioned in my previous post, Log::Log4perl->init() (or > init_and_watch) should only be called in the main program, not in > libraries or classes. Reason for this is that there can (currently) > only be *one* configuration file. If you call Log::Log4perl->init() > multiple times (what you will do implicitely if someone else doesn't > comply with this Log4perl mandate either), the last call to init() > will overwrite all previous settings. > Bottom line: Log::Log4perl->init() (or init_and_watch) should only > be called in the main program. > > 2) my $logger = Log::Log4perl->get_logger('BE'); > will always return a reference to the *same* object, no matter how > often you call it or which class/object instance you're calling it from. > This instance should *never* be stored anywhere or deleted. > Always retrieve them via get_logger(). > > 3) Your particular problem seems to be related with the way your accessors > work, I haven't tracked it down yet entirely, but it works with "regular" > instance variables. Tonight, I'm gonna look into it again, but it shouldn't > be relevant because you can fix the problem by fixing 1) and 2). > > -- Mike > > Mike Schilli > log...@pe... > http://perlmeister.com > http://log4perl.sourceforge.net -- Gordon Marler <gm...@gm...> |
From: <gm...@gm...> - 2003-06-04 15:17:16
|
The problem has been solved - I did it to myself. This boils down to the fact that the class BE keeps a class hash (implemented as an "our" or "my" lexical variable with package/file scope) that contains an array with references to each BE object instance. The reference to the array can be retrieved with BE->ALL. Any time a BE object goes out of scope, the @{BE->ALL} array still holds a reference to it, so DESTROY doesn't get to run until the entire program completes, at which time all other items in the interpreter (including Log::Log4perl) are already gone. Hence the problem, which is totally unrelated to Log::Log4perl. I've fixed the problem for two scenarios: 1. Normal program termination 2. Aborts (signals, etc) - All entries in @{BE->ALL} are made to be weak references - In the main program, I keep references to all of the objects I'm creating - In the case of normal program completed, I make sure to undef all of the object references, causing DESTROY to be run on them all before program termination - In the case of abnormal program termination, I wrap all object calls in eval blocks, so die'ing in the object returns control to the main program, which undef's each object before termination. Thus, DESTROY still gets to run properly for each object before program termination. I need to find a resource that describes how to do this more elegantly. Can't find a good reference for dealing with exceptions and handling object references properly in Object Oriented Perl. Any suggestions? At any rate, this is now determined NOT to be a problem with Log::Log4perl, unless someone who knows better thinks differently. Thanks for the comments that led to the final solution. Gordon Quoting Mike Schilli <log...@pe...>: > On Tue, 3 Jun 2003, Gordon Marler wrote: > > > # tri-natured: function, class method, or object method > > sub _classobj { > > my $objclass = shift || __PACKAGE__; > > my $class = ref($objclass) || $objclass; > > no strict "refs"; # to convert sym ref to real one > > return \%$class; > > } > > > > for my $datum (keys %{ _classobj() } ) { > > # turn off strict refs so that we can register a method > > # in the symbol table > > no strict "refs"; > > *$datum = sub { > > use strict "refs"; > > my $self = shift->_classobj(); > > $self->{$datum} = shift if @_; > > return $self->{$datum}; > > } > > } > > Ok, the previously posted suggestions should fix the problem described here, > but just out of curiosity: > > The reason for the warning message is very obscure. If you call the test > program test.pl, consisting of > > use BE; > $be = new BE; > undef $be; > print "\nfinished\n"; > > you'll get > > (in cleanup) Can't call method "log" on an undefined value at (eval 14) > line 37 during global destruction > > with the BE.pm code previously posted by Gordon and attached at the end of > this email. > > However, if you're using the debugger, and run it like > > perl -d test.pl > DB<1> r > > you'll just get > > finished > Debugged program terminated. Use q to quit or R to restart, > > Seems like the perl core gets confused with the reference count somehow. > Would be interesting if we can isolate the problem to a couple of lines > of code so we could submit it to p5p for further investigation ... > > -- Mike > > Mike Schilli > log...@pe... > http://perlmeister.com > http://log4perl.sourceforge.net > > > ############################################################ > # Code for BE.pm > ############################################################ > > package BE; > > use Log::Log4perl qw(:easy); > > %BE = ( > logger => "", > ConfigFile => "/etc/lb_lutab", > # List of all BE objects > ALL => [ ], > ); > > #tri-natured: function, class method, or object method > sub _classobj { > my $objclass = shift || __PACKAGE__; > my $class = ref($objclass) || $objclass; > no strict "refs"; # to convert sym ref to real one > return \%$class; > } > > for my $datum (keys %{ _classobj() } ) { > # turn off strict refs so that we can register a method > # in the symbol table > no strict "refs"; > *$datum = sub { > use strict "refs"; > my $self = shift->_classobj(); > $self->{$datum} = shift if @_; > return $self->{$datum}; > } > } > > # Constructor > sub new { > my ($caller,%arg) = @_; > my $caller_is_obj = ref($caller); > my $class = $caller_is_obj || $caller; > my ($logger); > > unless (BE->logger) { > Log::Log4perl->easy_init($DEBUG); > my $logger = Log::Log4perl->get_logger('BE'); > print "logger is $logger\n"; > BE->logger($logger); > } > > $logger = Log::Log4perl->get_logger('BE'); > > my $instance = { > _name => $arg{name} || # Name of this BE > undef, > #... Other members of the object ... > _logger => undef, > }; > > # Save a copy of BE->logger here so the reference to the logger can > # for use inside DESTROY > $instance->{_logger} = $logger; > > # Append this to the Class list of all BEs > my ($all) = BE->ALL(); > push @{$all}, $instance; > BE->ALL($all); > bless $instance, $class; > > } > > sub DESTROY { > my ($self) = @_; > my ($logger) = Log::Log4perl->get_logger("BE"); > $logger->info("Entering DESTROY method for " . __PACKAGE__ . "\n"); > } > > 1; > > ------------------------------------------------- This mail sent through IMP: http://horde.org/imp/ |
From: Mike S. <log...@pe...> - 2003-06-05 02:10:12
|
On Wed, 4 Jun 2003 gm...@gm... wrote: > This boils down to the fact that the class BE keeps a class hash (implemented as > an "our" or "my" lexical variable with package/file scope) that contains an > array with references to each BE object instance. The reference to the array > can be retrieved with BE->ALL. That explains most of it, although I'm still puzzled why the debugger doesn't show it. > Any time a BE object goes out of scope, the @{BE->ALL} array still holds a > reference to it, so DESTROY doesn't get to run until the entire program > completes, at which time all other items in the interpreter (including > Log::Log4perl) are already gone. Hence the problem, which is totally unrelated > to Log::Log4perl. > > I've fixed the problem for two scenarios: How about a wrapper class, each object carrying a BE object? If those wrapper objects go out of scope, their DESTROY is called, and you can do the cleanup of the contained BE object manually (e.g. by removing the entry in ALL). -- Mike Mike Schilli log...@pe... http://perlmeister.com http://log4perl.sourceforge.net |