http://qs321.pair.com?node_id=11110731

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

So I want a logger object, and only one logger object thought it may be used in various packages. I follow the simple instructions at https://www.perl.com/article/52/2013/12/11/Implementing-the-singleton-pattern-in-Perl but when I test it I see I get two objects. In the following test one gets its 'Log' value altered and the other does not ...

The test ...

perl -MData::Dumper -Mlib=. -MHelper::Lgr -E' \ $l=Helper::Lgr->getInstance();$l->log(0,"Log1"); \ $z=Helper::Lgr->getInstance();$l->set('Log', 4); \ print Dumper $l; \ print Dumper $z' Get instance called from main Not yet instantiated Sun Dec 29 18:51:08 2019 Log1 Get instance called from main Not yet instantiated $VAR1 = bless( { 'LogName' => 'CommandLine.log', '_continuation' => 0, 'Screen' => 0, 'LogDir' => '/home/random/log', 'fh' => \*Helper::Lgr::__ANONIO__, 'Log' => 4 }, 'Helper::Lgr' ); $VAR1 = bless( { 'LogDir' => '/home/random/log', 'Screen' => 0, 'LogName' => 'CommandLine.log', '_continuation' => 0, 'Log' => 0, 'fh' => \*Helper::Lgr::__ANONIO__ }, 'Helper::Lgr' );

Here is the start of the Logger pm that I am using, the set method is pretty much what you expect,so not reprduced here ...

#!/usr/bin/perl # # Lgr.pm # # Controls both logging to a file and to the screen # you can set a verbosity for each independently # package Helper::Lgr; use strict; use warnings; use v5.10.0; use File::Basename; use File::Path qw(make_path); use FindBin; use Data::Dumper; my $script = basename( $0, '.pl', '.pm' ); # Logger is a classic singleton pattern. We only want one instance in +our code my $instance = undef; sub getInstance { say "Get instance called from ".( caller ); if ($instance) { # Already instantiated, check if we need to chang +e any settings say "Already instantiated"; return $instance; } else { say "Not yet instantiated"; } my $class = shift; my $instance = shift; # settings to use $instance = {} unless $instance; die "$instance is not a hash ref\n" unless ref $instance eq 'HASH' +; bless $instance, $class; # we bless early so we can call methods o +n ourself ... And much more ...
What simple fact am I missing that I get a fresh logger each time?

Cheers,
R.

Pereant, qui ante nos nostra dixerunt!

Replies are listed 'Best First'.
Re: My promiscous singleton
by tobyink (Canon) on Dec 29, 2019 at 18:16 UTC

    You declare two different variables called $instance.

      You are right. I had gone blind looking at complicated issues of variable scope and missed that which was staring me in the face. Thanks.

      Cheers,
      R.

      Pereant, qui ante nos nostra dixerunt!
Re: My promiscous singleton
by kikuchiyo (Hermit) on Dec 29, 2019 at 18:09 UTC

    Probably this:

    my $instance = shift; # settings to use $instance = {} unless $instance;

      Spot on sir. Face palm time. Thanks for pointing that out.

      Now I remove that extra my it all works as desired:

      Get instance called from main Not yet instantiated $VAR1 = bless( { 'Log' => 0, 'Screen' => 0 }, 'Helper::Logger' ); Mon Dec 30 01:25:32 2019 Log1 Get instance called from main Already instantiated Setting new values $VAR1 = bless( { 'Log' => 4, 'fh' => \*Helper::Logger::__ANONIO__, 'LogName' => 'CommandLine.log', 'Screen' => 0, 'LogDir' => '/home/random/log', '_continuation' => 0 }, 'Helper::Logger' ); $VAR1 = bless( { 'Log' => 4, 'fh' => \*Helper::Logger::__ANONIO__, 'LogName' => 'CommandLine.log', 'Screen' => 0, 'LogDir' => '/home/random/log', '_continuation' => 0 }, 'Helper::Logger' );

      Both nicely point to the same object. Perfect

      Thanks,
      R.

      Pereant, qui ante nos nostra dixerunt!
Re: My promiscous singleton
by 1nickt (Canon) on Dec 29, 2019 at 18:07 UTC

    Hi, not really an answer to your question about globals, but:

    "So I want a logger object, and only one logger object though it may be used in various packages ... Controls both logging to a file and to the screen ... you can set a verbosity for each independently

    Unless you are doing this a learning exercise, why not use Log::Any and the Log::Any::Adapter::Dispatch output adapter which allows you to do exactly what you specified, out of the box?

    Hope this helps!


    The way forward always starts with a minimal test.

      Because it is a secure environment and I may not use any non core modules, other than those I write...

      I know, I know, but sometimes it's not worth the fight. And believe me it would take months, if not years/for ever to get sign off. But thanks for the common sense answer

      Cheers,
      R.

      Pereant, qui ante nos nostra dixerunt!
        Because it is a secure environment and I may not use any non core modules, other than those I write...

        Who came up with that nonsense idea? What makes core modules so special that they are suitable for a "secure" environment, but other modules from CPAN aren't? And what about that "insecure" CPAN modules that become core modules during the development of Perl? How do they suddenly become "secure"? Is the entire Perl source code subject to a code review?

        Or is "security" once more a lame excuse not to use CPAN? See also Re^4: CSV file with double quotes and NIH syndrome.

        Alexander

        --
        Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)