1

I want to write a small subroutine that can decorate all error messages in a consistent way instead of having to copy it all around my program.

However I want the line numbers to be from where it was called, not from where the die/warn occurred.

In C I would just use a preprocessor macro, but Perl does not have those. What is the best way to implement this?

Borodin
  • 126,100
  • 9
  • 70
  • 144
Prgrm.celeritas
  • 311
  • 2
  • 12
  • 1
    What do you mean "_from where it was called_"? If a sub throws an error/warning you want to see the line where the sub is called? You are looking for [Carp](http://perldoc.perl.org/Carp.html). – zdim May 16 '17 at 20:42
  • Then, in order to uniformly format you can use `$SIG{__WARN__}` and `$SIG{__DIE__}`. – zdim May 16 '17 at 20:48
  • 2
    You know that a program shouldn't ordinarily produce error messages, right? You should see them only during development, and at that point, as long as they're clear, you shouldn't be concerned about how cute they look. (Although I think Perl messages are pretty consistent as they are.) If you're getting them after release, and especially if you're getting so many of them that their inconsistency is apparent, then you haven't completed testing. – Borodin May 16 '17 at 20:55
  • @zdim: I think your first comment is going to be the closest thing to an answer that we have. – Borodin May 16 '17 at 20:58
  • @Borodin What do you mean ... that it would be misleading/wrong posting answers onthis? (I just did, but I can remove it...) Or that the question is unclear/under-specified? – zdim May 16 '17 at 21:00
  • 1
    *"In C I would just use a preprocessor macro, but Perl does not have those."* If you get hold of Perl v5.10 then you can use `-P` on the command line or the shebang line to do just that. *"This option causes your program to be run through the C preprocessor before compilation by Perl."* But it was removed in version 12 because it was a dreadful idea. – Borodin May 16 '17 at 21:08
  • @zdim: I meant that you should write it up as an answer because I doubt if anyone is going to do better! – Borodin May 16 '17 at 21:11
  • If you're interested in preprocessing Perl a little more sensibly then you could look at [Filter::Simple](https://metacpan.org/pod/Filter::Simple) which essentially allows you to apply a set of substitutions to your code before it's compiled. It's still rather unpleasant, but only for the reasons that the C preprocessor is unpleasant. – Borodin May 16 '17 at 21:15
  • @Borodin Thanks for the tip. Though I think the __WARN__ and __DIE__ signals will be sufficient. – Prgrm.celeritas May 16 '17 at 21:26
  • @Prgrm.celeritas: I wasn't just putting you down in my first comment: I expected you to come back with a more detailed explanation of what you meant. Please would you explain why you want to *"decorate all error messages"*? (By the way, put things like `__WARN__` inside bacticks to avoid the contents being treated as markdown, especially if it's a code fragment anyway.) – Borodin May 16 '17 at 21:49
  • @Borodin Our standard for warning messages is "\n-W- ($0) \n". Similar for error messages. Problem is, people often forget parts of the standard which leads to inconsistent log files and bugs. I wanted to add the prefix and suffix to a function so you could just give a warning or error and not worry about formatting. – Prgrm.celeritas May 16 '17 at 22:23
  • @Prgrm.celeritas Thanks for clarification. While this should cit it for a simple solution, have you considered [Log::Log4Perl](http://search.cpan.org/~mschilli/Log-Log4perl-1.49/lib/Log/Log4perl.pm)? It's heavy-handed for just this, but great if more is (or can be) standardized. – zdim May 16 '17 at 22:24
  • @Prgrm.celeritas: Thank you. But I still don't see how you can expect any warning or error messages at all from a properly-written program. If you want to record stuff like "disk full" or "connection time out" then you probably shouldn't use `warn` and `die`. – Borodin May 16 '17 at 22:45
  • 1
    @Borodin I am surprised you would take that view. A warning could be something like, "module does not support this feature" or "spec files are outdated". An error could be something like "invalid configuration" or "spec files missing necessary param x". Those seem like perfectly valid messages to me, even in release code. – Prgrm.celeritas May 16 '17 at 23:05
  • @Prgrm.celeritas: *module does not support this feature* is clearly a bug, if I understand you correctly. I don't know what you mean by specification files, which to me are design documents. I agree with you about *invalid configuration*, along with, as I suggested, *disk full* and *connection time out*, but together those are so few and so rare that it seems a waste of time to write a special formatter for them when a logger would be more appropriate anyway. – Borodin May 17 '17 at 11:52
  • @Borodin Sorry should have clarified: Hardware modules, not Perl modules. And spec files describe the specifications, parameterization, and configurations of those modules. – Prgrm.celeritas May 17 '17 at 12:37

2 Answers2

8

Use Carp for warnings/errors. You can use __WARN__ and __DIE__ hooks to affect what warn prints to the STDERR stream and how die is thrown. Note that they are quite different.

use warnings;
use strict;
use feature 'say';

use Carp;

$SIG{__WARN__} = \&wrap_warn;

sub wrap_warn {
    print "WARNING: @_";
} 

sub level_2 { 
    say "Print from level_2";
    carp "carp from level_2(@_)"; 
}

sub level_1 {
    level_2(@_);
}

level_1("from main");

prints to STDOUT

Print from level_2
WARNING: carp from level_2(from main) at wrap_warn.pl line 19.
        main::level_2('from main') called at wrap_warn.pl line 15
        main::level_1('from main') called at wrap_warn.pl line 22

If you want this to still go to STDERR then use print STDERR "WARNING: @_";

Make sure to carefully read about %SIG in perlvar and warn, at least.


While it seems that you want this to be global, I'd like to mention that one generally wants to local-ize changes like this one. There's an example in this post, and more out there.

zdim
  • 64,580
  • 5
  • 52
  • 81
3

Adding the following will add stack traces to exceptions and warnings:

use Carp::Always;

For the occasional use, use the following to avoid modifying your program:

perl -MCarp::Always script args

or

PERL5OPT=-MCarp::Always script args
ikegami
  • 367,544
  • 15
  • 269
  • 518