Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
PerlMonks  

Override printing to STDOUT/ERR

by bliako (Prior)
on Apr 13, 2018 at 12:53 UTC ( #1212802=perlquestion: print w/replies, xml ) Need Help??

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

Hello wise Monks,

I would like to override Perl's printing to STDOUT/STDERR with my own version which simply adds the line number the print function call happened. Ideally, ALL the line numbers/filenames/packagenames in the case of nested calls all the way to the top/main because I want to separate a program's STDOUT depending on which part of the program it originated.

You can assume I have complete control over the source code (of the program which I want to override its print statements) and can change anything I like. HOWEVER, I do not have control of the source-code of the many packages the program may be using and the print statements therein.

Initially I thought to just replace print with my_own_print in the source code and run that. Then I realised that it should only affect print in a function-call context and I used PPI for this purpose. And then I realised that there are other ways of printing to STDOUT/STDERR including the croaks etc. And finally I also realised that print statements in the packages will be out of my control. So this plan collapsed fairly quickly.

Now, I have already read the thread !Overriding Builtin print

and the more recent https://stackoverflow.com/questions/387702/how-can-i-hook-into-perls-print/6281636

However, I wonder if there are any recent developments since then or a definite HOWTO.

thanks, bliako

Replies are listed 'Best First'.
Re: Override printing to STDOUT/ERR
by Corion (Pope) on Apr 13, 2018 at 13:10 UTC

      thanks for the recommendation. Following your suggestion I stumbled upon  Tie::STDOUT (by David Cantrell) which does the trick:

      #!/usr/bin/env perl use strict; use warnings; use Tie::STDOUT print => sub { my @params = @_; my @calhis = (); my $i = 2; # to avoid printing stacktrace for Tie::STDOUT # build a kind of stacktrace: while( my ($package, $filename, $line, $subroutine, $hasargs, $wantarray, $evaltext, $is_require, $hints, $bitmask, $hinthash) = caller($i++) ){ push(@calhis, "$filename, line $line : $package".'::'."$subrou +tine"); } # print the stacktrace with whatever needs to be printed print join("->", @calhis), " : ", @_; } ; print "from main\n"; sub1("main"); sub2("main"); sub sub1 { my $via = $_[0]; print "from sub1 via $via\n"; } sub sub2 { sub1("sub2"); }
Re: Override printing to STDOUT/ERR
by LanX (Cardinal) on Apr 13, 2018 at 13:57 UTC
    I'm still confused about the use case....

    If this is only for debugging purpose to find the line numbers , you can close STDOUT and STDERR and cause warnings. But the string to be printed will be lost.

    DB<6> close STDOUT DB<7> use warnings; print 666 print() on closed filehandle STDOUT at (eval 15)

    Otherwise I concur with Corion to tie a filehandle. caller will tell you where the tied method was called and you can redirect STDOUT and STDERR to these FHs. (also with select )

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Wikisyntax for the Monastery

      What I would like to do eventually is to be able to group program output (to STDOUT) depending on which program block produced it.

      Maybe visualising program output as a tree/graph. Which could also be used to selectively switch on and off program output, highligthing certain sections etc. Lots of possibilities.

      I will give more details in the Meditations section once I have a prototype.

      thanks for the input, bliako

        You may want to consider using on of the logging frameworks, available on CPAN, as opposed to a print based approach

        Log::Log4perl, Log::Any, Log::ger are examples that support directing output of the log statements to different locations based upon a category. A category is generally a package but can be explicitly set as desired.

        Here an example from the Log::ger::Manual::Tutorial::05_Category tutorial

        use Log::ger::Output Composite => ( outputs => { File => [ { conf => {path=>'/path/app.log'}, category_level => { Foo => 'off' }, }, { conf => {path=>'/path/foo.log'}, level => 'off', category_level => { Foo => 'trace' }, }, ], }, );

        lbe

Re: Override printing to STDOUT/ERR
by Tux (Abbot) on Apr 17, 2018 at 10:10 UTC

    Maybe Capture::Tiny serves your purpose enough:

    $ perl -MCapture::Tiny=capture -wE'my($out,$err,$exit)=capture{say"Hel +lo";warn"Die\n";};say"OUT:$out";say"ERR:$err";say"EXIT:$exit";' OUT:Hello ERR:Die EXIT:1

    Enjoy, Have FUN! H.Merijn
      While Capture::Tiny is simpler, the OP wanted to separate output according to the package/file the print() originates. So in this case, a tie-based solution like Tie::STDOUT is more appropriate as it lets you call your custom code on every print() or printf() or syswrite(), and then you can use caller() to extract the source location of each print() and act accordingly.
Re: Override printing to STDOUT/ERR
by LanX (Cardinal) on Apr 13, 2018 at 13:30 UTC
      yeah but what about the print from 3rd party packages?

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1212802]
Approved by marto
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others scrutinizing the Monastery: (3)
As of 2020-10-25 06:55 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    My favourite web site is:












    Results (249 votes). Check out past polls.

    Notices?