Αποσφαλμάτωση με print, η warn, και η οικογένεια Carp#
Αυτό το κεφάλαιο καλύπτει μη διαδραστικά διαγνωστικά: εισαγωγή print, dump μεταβλητών, ίχνη στοίβας από μέσα στο πρόγραμμα. Αυτές οι τεχνικές παραμένουν ο γρηγορότερος δρόμος για να καταλάβετε «τι βλέπει πραγματικά ο κώδικάς μου εδώ;» για σφάλματα που αναπαράγονται εύκολα.
Οι αναγνώστες που γνωρίζουν ήδη τις print και warn θα βρουν εδώ τις δομημένες παραλλαγές που αξίζουν τη χρήση τους: την οικογένεια Carp που αναφέρει από τον καλούντα, το Data::Dumper και τους σύγχρονους αντικαταστάτες του, και τα μονόγραμμα για ίχνος στοίβας παντού.
warn έναντι print - επιλέξτε τη σωστή ροή#
Η warn γράφει στο STDERR· η print γράφει εξ ορισμού στο STDOUT. Χρησιμοποιήστε την warn για διαγνωστικά: διατηρεί την έξοδο του προγράμματος καθαρή, επιβιώνει της ανακατεύθυνσης του STDOUT, και είναι η ροή που οι loggers ήδη συλλαμβάνουν.
warn "*** foo=[$foo] bar=[@bar]\n";
Τυλίξτε τις τιμές σε κυριολεκτικές αγκύλες ώστε τα τελικά κενά και τα newline να είναι ορατά στην έξοδο. Το "foo=[$foo]" αποκαλύπτει foo=[ ] εκεί που το "foo=$foo" δείχνει απλώς foo= .
Χωρίς τελικό \n, η warn (και η die) προσθέτουν at FILE line N. από την οπτική του καλούντος. Με \n, δεν το κάνουν. Πρακτικός κανόνας: προσθέστε \n για μηνύματα προς τον χρήστη· παραλείψτε το για διαγνωστικά προγραμματιστή όπου θέλετε τη θέση.
Φτηνές ετικέτες θέσης από τον μεταγλωττιστή:
warn "reached $0 " . __FILE__ . ':' . __LINE__ . "\n";
warn "in @{[__SUB__->name]}\n"; # 5.16+
Η οικογένεια Carp - αναφορά από τον καλούντα#
Όταν μια συνάρτηση βιβλιοθήκης αποτυγχάνει, το να δείχνει στον δικό της κώδικα σχεδόν ποτέ δεν είναι χρήσιμο. Η οικογένεια Carp αναφέρει από το πλαίσιο του καλούντος:
Συνάρτηση | Αναφέρει από | Μοιραίο; | Ίχνος στοίβας; |
|---|---|---|---|
| caller | όχι | όχι |
| caller | ναι | όχι |
| caller | όχι | πλήρες |
| caller | ναι | πλήρες |
use Carp;
sub open_config {
my $path = shift // croak "open_config: missing path";
open my $fh, '<', $path or croak "cannot read $path: $!";
return $fh;
}
Κώδικας βιβλιοθήκης πρέπει να χρησιμοποιεί croak/carp, όχι die/warn - το μήνυμα σφάλματος δείχνει στη γραμμή του καλούντος, εκεί που πρέπει να πάει η διόρθωση.
Ίχνη στοίβας χωρίς να αγγίξετε τον κώδικα#
Το Carp::Always εγκαθιστά τους $SIG{__DIE__} και $SIG{__WARN__} ώστε να δρομολογούν μέσω των Carp::confess / Carp::cluck. Τρέξτε ένα προβληματικό σενάριο με:
perl -MCarp::Always script.pl
Κάθε die και warn παράγει πλήρη στοίβα. Χωρίς αλλαγές στον κώδικα. Αυτό είναι το πρώτο πράγμα στο οποίο καταφεύγει κανείς όταν μια κατάρρευση δίνει ένα άχρηστο μονόγραμμο μήνυμα.
Το Devel::Confess είναι απευθείας ισοδύναμος αντικαταστάτης με λιγότερες οριακές περιπτώσεις γύρω από overloaded αντικείμενα:
perl -d:Confess script.pl
Και τα δύο αρθρώματα προσθέτουν κόστος σε κάθε warn και die· χρησιμοποιήστε τα κατά τη διερεύνηση, όχι σε σταθερή παραγωγή.
Για μόνιμη εγκατάσταση μέσα στον κώδικα:
use Carp;
$SIG{__DIE__} = sub { return if $^S; Carp::confess(@_) };
$SIG{__WARN__} = sub { Carp::cluck(@_) };
Το return if $^S; - «επιστροφή αν είμαστε μέσα σε eval». Χωρίς αυτόν τον φύλακα κάθε παγιδευμένη εξαίρεση πληρώνει το κόστος του ίχνους. Δείτε exceptions για το πλήρες μοτίβο $SIG{__DIE__}.
Dump δομών δεδομένων#
Περάστε αναφορά σε κάθε dumper· το dump γυμνού κατακερματισμού ή πίνακα εκτυπώνει την επίπεδη μορφή λίστας και χάνει τη δομή.
Data::Dumper - καθολικό, στον πυρήνα, με δυνατότητα πλήρους κύκλου μέσω eval#
use Data::Dumper;
$Data::Dumper::Sortkeys = 1;
$Data::Dumper::Indent = 1;
$Data::Dumper::Terse = 1;
$Data::Dumper::Deepcopy = 1;
warn Dumper(\%config);
Το
Sortkeysκαθιστά την έξοδο σταθερή σε diff.Το
Indent=1είναι η ευανάγνωστη ρύθμιση (0 = επίπεδο, 2 = προεπιλογή, 3 = με δείκτες πίνακα).Το
Terse=1καταστέλλει τα προθέματα$VAR1 =όταν δεν χρειάζεστε δυνατότητα πλήρους κύκλου.Το
Deepcopy=1απενεργοποιεί την παρακολούθηση «έχω ξαναδεί αυτή την αναφορά»$VAR1->{...}- εκτυπώνει τα δεδομένα, όχι μια ανακατασκευή γράφου.
Το Data::Dumper::Concise είναι απευθείας αντικαταστάτης με λογικές προεπιλογές προεφαρμοσμένες· το πρότυπο της κοινότητας DBIx::Class / Moose.
Data::Printer - έγχρωμο, σύγχρονο, ευανάγνωστο#
use DDP;
p %config; # not a reference - DDP handles both
Το Data::Printer εκτυπώνει εμφανισιακά καλύτερη έξοδο από το Data::Dumper, χρωματίζει ανά τύπο, χειρίζεται ειδικά τα αντικείμενα (κλάση, γνωρίσματα, overloads), και δεν επιχειρεί πλήρη κύκλο. Προτιμήστε το όταν ο στόχος είναι η ανάγνωση του dump, όχι η εκ νέου εισαγωγή του.
Data::Dump - συμπαγές μονόγραμμο#
use Data::Dump qw(dd);
dd \%config; # prints to STDERR in compact form
Το Data::Dump του Gisle Aas. Χρήσιμο όταν η προεπιλεγμένη έξοδος του Data::Dumper είναι πολύ φλύαρη για μία γραμμή warn.
Πότε να καταφεύγει κανείς σε ποιο#
Data::Dumperόταν ενδέχεται να τροφοδοτήσετε την έξοδο πίσω σεevalή να συγκρίνετε δύο dump.Data::Printer(DDP) όταν την έξοδο τη διαβάζει άνθρωπος.Data::Dumpόταν θέλετε σύνοψη μιας γραμμής.Data::Dumper::Conciseσε έργα που ήδη το χρησιμοποιούν.
Print εντοπισμού σφαλμάτων υπό συνθήκη#
Μια σταθερά κατά τη μεταγλώττιση βελτιστοποιείται πλήρως όταν είναι ψευδής:
use constant DEBUG => 0;
warn "state=$state\n" if DEBUG;
Με DEBUG => 0, ο έλεγχος if DEBUG πτυχώνεται κατά τη μεταγλώττιση και η warn δεν εμφανίζεται ποτέ στο δέντρο op - μηδενικό κόστος χρόνου εκτέλεσης.
Συνδέστε στο περιβάλλον ώστε η σημαία να αλλάζει χωρίς επεξεργασία:
use constant DEBUG => $ENV{APP_DEBUG} // 0;
Πολλαπλά κανάλια μέσω μάσκας bits:
use constant { WEB => 1, SQL => 2, REGEX => 4 };
my $DEBUG = $ENV{APP_DEBUG} // 0;
sub dbg { my $c = shift; warn "[DBG] @_\n" if $DEBUG & $c }
dbg WEB, "request $req_id start\n";
dbg SQL, "query: $sql\n";
dbg REGEX, "matched: $&\n"; # (unless you care about perf - see below)
Εναλλακτική με φίλτρο πηγαίου: Smart::Comments#
use Smart::Comments;
my @stuff = compute();
### @stuff # prints: @stuff: [...]
### Checking key: $key # print, plus asserts $key is truthy
### Processing |=| ... for @stuff
Γραμμές που ξεκινούν με ### ξαναγράφονται ως print (και, με ειδική σύνταξη, μπάρες προόδου και διεκδικήσεις). Το άρθρωμα είναι φίλτρο πηγαίου - ξαναγράφει το κείμενο του προγράμματος πριν τη μεταγλώττιση - οπότε οι αναφερόμενοι αριθμοί γραμμών μπορούν να μετατοπιστούν κατά ένα. Όταν δεν εισάγεται, τα σχόλια ### μεταγλωττίζονται σε τίποτα. Η μετατόπιση είναι το τίμημα του να μη γεμίζει ο κώδικας με ρητές γραμμές warn.
Taint και $& - δευτερεύοντα κόστη που πρέπει να γνωρίζετε#
Η αναφορά στα $&, $`, ή $' οπουδήποτε στο πρόγραμμα αναγκάζει την perl να διατηρεί αυτές τις καθολικές μεταβλητές περιβάλλοντος αντιστοιχίας για κάθε αντιστοιχία regex σε όλο το πρόγραμμα, συμπεριλαμβανομένων αντιστοιχιών μέσα σε φορτωμένα αρθρώματα. Ιστορικά αυτό επέβαλλε αισθητή επιβράδυνση· στις σύγχρονες perl το κόστος είναι μικρότερο αλλά μη μηδενικό, και ο κανόνας ισχύει ακόμη: μην τις αγγίζετε αν μπορείτε να το αποφύγετε.
Χρησιμοποιήστε τα @- / @+ (πίνακες θέσεων αντιστοιχίας), ονομαστικές συλλήψεις, ή τον τροποποιητή /p με τα ${^MATCH}, ${^PREMATCH}, ${^POSTMATCH} αντί αυτών. Οι παραλλαγές /p επιβάλλουν το κόστος μόνο για αντιστοιχίες που το επιλέγουν.
Καταγραφή με χρονική σφραγίδα#
Μια warn φτιαγμένη με το χέρι με localtime είναι επαρκής για εφάπαξ διερευνήσεις:
use Time::HiRes qw(gettimeofday);
sub t { my ($s, $us) = gettimeofday; sprintf "%d.%06d", $s, $us }
warn sprintf "[%s] state=%s\n", t(), $state;
Για οτιδήποτε πιο δομημένο, χρησιμοποιήστε ένα framework καταγραφής αντί να φτιάχνετε δικό σας:
Log::Any- ασφαλές για βιβλιοθήκες· εκπέμπει logs χωρίς να επιλέγει backend. Συνδυάστε με προσαρμογέα (Log::Any::Adapter::Log4perl,::Stdout,::Syslog) στην εφαρμογή.Log::Log4perl- κατηγορία / επίπεδο / appender / διάταξη· καθοδηγείται από αρχείο ρυθμίσεων.Log::Dispatch- δρομολογητής χαμηλότερου επιπέδου· συχνά χρησιμοποιείται κάτω από τοLog::Any.
Πρότυπα επίπεδα, από το χαμηλότερο στο υψηλότερο: TRACE, DEBUG, INFO, WARN, ERROR, FATAL. Φυλάξτε τα TRACE / DEBUG πίσω από μια σημαία περιβάλλοντος στην παραγωγή.
Μάθετε περισσότερα#
exceptions -
die,$@,$SIG{__DIE__}, τυποποιημένες κλάσεις εξαιρέσεων, το σύγχρονοtry/catch.interactive-debugger - όταν η εισαγωγή print κοστίζει περισσότερο από την εκκίνηση του
perl -d.tracing - ίχνος γραμμή-προς-γραμμή ολόκληρου του προγράμματος χωρίς επεξεργασία του πηγαίου.