Inspired by ysth's Debugging with tied variables node, and motivated by a desire to dig deeper into understanding the mechanics of creating an object-oriented package, I set out to create Tie::Scalar::Logged. I am posting this as a Meditation, because I don't consider it a work-complete, but rather a stage in the process upon which I would like to reflect and possibly gather additional input.

The following code snippet allows you to tie a scalar to a logfile. You may choose which types of activities will be logged (default is ALL: TIESCALAR, STORE, FETCH, and DESTROY). A log file will be created (if it doesn't already exist), and any activity related to the tied scalar will be scribbled into the logfile for later review.

The scalar should behave just like any old normal scalar, but with the side effect that any use of the tied scalar will result in another line being written into the logfile.

From this starting framework, it shouldn't be too difficult to also create Tie::Hash::Logged, and Tie::Array::Logged.

Here is the code, along with a sample usage:


package Tie::Scalar::Logged; use IO::File; require Tie::Scalar; @ISA = qw/Tie::StdScalar/; use strict; use warnings; # TIESCALAR: Mostly self-explanatory, but just to be # thorough, $logfile is the filename of the output file. # $name is a single-quoted string naming the variable that # has been tied. This is for reporting purposes only. # $events is an optional array ref that should contain # the names of the methods you would like to track. Method # names will be case insensitive. They may include: # ALL, TIESCALAR, STORE, FETCH, DESTROY. sub TIESCALAR { my ( $class, $logfile, $name, $events ) = @_; my $self = {}; $self->{Name} = $name; $self->{LOG} = new IO::File ">> $logfile" or return undef; $self->{Value} = undef; if ( not defined ( $events ) or grep {/ALL/i} @$events ) { @$events = qw/TIESCALAR STORE FETCH DESTROY/; } $self->{uc($_)}=1 foreach @$events; print {$self->{LOG}} "$self->{Name} => TIESCALAR\tLogging to $logfile\n" if $self->{TIESCALAR}; bless $self, $class; } sub STORE { my ( $self, $value ) = @_; $self->{Value} = $value; print {$self->{LOG}} "$self->{Name} => STORE\t\tValue = $value\n" if $self->{STORE}; return $self->{Value}; } sub FETCH { my $self = shift; print {$self->{LOG}} "$self->{Name} => FETCH\t\tValue = $self->{Value}\n" if $self->{FETCH}; return $self->{Value}; } sub DESTROY { my $self = shift; print {$self->{LOG}} "$self->{Name} => DESTROY\t\tValue = $self->{Value}\n" if $self->{DESTROY}; $self->{LOG}->close; } 1; # ---------- Begin main ---------- package main; use strict; use warnings; # Simple test: Create a lexical variable and perform a few # operations on it. The logfile will then contain a log of # all activity affecting the variable. my $var; # When tieing a variable to Tie::Scalar::Logged, the parameter list # is as follows: Variable to tie, Package name, Logfile name, # Single-quoted string naming variable that is being tied, and # finally, an optional reference to an array containing one or # more of the following activities that can be logged: # ALL, TIESCALAR, FETCH, STORE, DESTROY. (Case/Order insensitive.) tie $var, "Tie::Scalar::Logged", 'log.txt', '$var', [qw/all/]; $var = 10; $var += 10; print $var, "\n";


I am interested in reading comments that might lead to improvement. One thing in particular is I would be interested in finding a way (if it is possible) of also logging the line-number where the activity being logged took place. Maybe if there's an interest in this module I can figure out what it takes to get it ready to be put on CPAN.

I should take a moment to thank those of you who, in the Chatterbox, helped me fumble my way through my first attempt at an OO module. I really appreciate the learning opportunities I gain from this site, and the teaching-moments I gain from so many of this site's regulars.

Update: Changed

$self->{LOG} = new IO::File ">> $logfile" or die "Can't open $logfile for append: $!\n";

to or return undef; for a more graceful reaction if the logfile is unable to be opened. Thanks duff and ysth for the suggestion.

Changed module name from Tie::Scalar::Log to Tie::Scalar::Logged per duff's recommendation.


Dave


In reply to Scalars tied to logfiles by davido

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.