# Print debugging, `warn`, and the Carp family This chapter covers non-interactive diagnostics: inserted prints, variable dumps, stack traces from inside the program. These techniques remain the fastest path to understanding "what does my code actually see here?" for bugs that are cheap to reproduce. Readers who already know about [`print`](../../p5/core/perlfunc/print) and [`warn`](../../p5/core/perlfunc/warn) will find here the structured variants that earn their keep: the Carp caller-reporting family, `Data::Dumper` and its modern replacements, and the stack-trace-everywhere one-liners. ## `warn` vs `print` — pick the right stream [`warn`](../../p5/core/perlfunc/warn) writes to STDERR; [`print`](../../p5/core/perlfunc/print) defaults to STDOUT. Use `warn` for diagnostics: it keeps program output clean, survives STDOUT redirection, and is the stream loggers already capture. ```perl warn "*** foo=[$foo] bar=[@bar]\n"; ``` Wrap values in literal brackets so trailing whitespace and newlines are visible in the output. `"foo=[$foo]"` reveals `foo=[ ]` where `"foo=$foo"` just shows `foo= `. Without a trailing `\n`, `warn` (and `die`) append `at FILE line N.` from the caller's perspective. With `\n`, they do not. Rule of thumb: append `\n` for user-facing messages; omit it for developer diagnostics where you want location. Cheap location tags from the compiler: ```perl warn "reached $0 " . __FILE__ . ':' . __LINE__ . "\n"; warn "in @{[__SUB__->name]}\n"; # 5.16+ ``` ## The Carp family — report from the caller When a library function fails, pointing at the library's own source is almost never useful. `Carp`'s family reports from the caller's frame: | Function | Reports from | Fatal? | Stack trace? | |-----------|--------------|--------|--------------| | `carp` | caller | no | no | | `croak` | caller | yes | no | | `cluck` | caller | no | full | | `confess` | caller | yes | full | ```perl use Carp; sub open_config { my $path = shift // croak "open_config: missing path"; open my $fh, '<', $path or croak "cannot read $path: $!"; return $fh; } ``` Library code should use `croak`/`carp`, not `die`/`warn` — the error message points at the caller's line, which is where the fix goes. ## Stack traces without touching the code `Carp::Always` installs `$SIG{__DIE__}` and `$SIG{__WARN__}` to route through `Carp::confess` / `Carp::cluck`. Run a misbehaving script with: ``` perl -MCarp::Always script.pl ``` Every `die` and `warn` produces a full stack. No source changes. This is the first thing to reach for when a crash gives a useless one-line message. `Devel::Confess` is a drop-in equivalent with fewer edge cases around overloaded objects: ``` perl -d:Confess script.pl ``` Both modules add cost to every `warn` and `die`; use them while investigating, not in steady production. For a permanent in-code install: ```perl use Carp; $SIG{__DIE__} = sub { return if $^S; Carp::confess(@_) }; $SIG{__WARN__} = sub { Carp::cluck(@_) }; ``` `return if $^S;` — "return if we are inside `eval`". Without this guard every caught exception pays the trace cost. See [exceptions](exceptions) for the full `$SIG{__DIE__}` pattern. ## Dumping data structures Pass a **reference** to every dumper; dumping a bare hash or array prints its flat-list form and loses the structure. ### `Data::Dumper` — universal, core, `eval`-roundtrippable ```perl use Data::Dumper; $Data::Dumper::Sortkeys = 1; $Data::Dumper::Indent = 1; $Data::Dumper::Terse = 1; $Data::Dumper::Deepcopy = 1; warn Dumper(\%config); ``` - `Sortkeys` makes output diff-stable. - `Indent=1` is the readable setting (0 = flat, 2 = default, 3 = with array indices). - `Terse=1` suppresses `$VAR1 =` prefixes when you do not need round-trip-ability. - `Deepcopy=1` disables the "seen this ref before" `$VAR1->{...}` tracking — prints the data, not a graph reconstruction. `Data::Dumper::Concise` is a drop-in with sensible defaults pre-applied; the DBIx::Class / Moose community standard. ### `Data::Printer` — coloured, modern, readable ```perl use DDP; p %config; # not a reference — DDP handles both ``` `Data::Printer` prints better-looking output than `Data::Dumper`, colours by type, handles objects specially (class, attributes, overloads), and does not try to round-trip. Prefer it when the goal is reading the dump, not re-ingesting it. ### `Data::Dump` — compact one-liner ```perl use Data::Dump qw(dd); dd \%config; # prints to STDERR in compact form ``` Gisle Aas's `Data::Dump`. Useful when the default `Data::Dumper` output is too verbose for a single warn line. ### When to reach for which - `Data::Dumper` when you might feed the output back into [`eval`](../../p5/core/perlfunc/eval) or compare two dumps. - `Data::Printer` (DDP) when a human is reading the output. - `Data::Dump` when you want a one-line summary. - `Data::Dumper::Concise` on projects already using it. ## Conditional debug prints A compile-time constant is optimised out entirely when false: ```perl use constant DEBUG => 0; warn "state=$state\n" if DEBUG; ``` With `DEBUG => 0`, the `if DEBUG` test is folded at compile time and the `warn` never appears in the op tree — zero runtime cost. Wire to the environment so the flag is flippable without editing: ```perl use constant DEBUG => $ENV{APP_DEBUG} // 0; ``` Multiple channels via bitmask: ```perl 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) ``` ## Source-filter alternative: `Smart::Comments` ```perl use Smart::Comments; my @stuff = compute(); ### @stuff # prints: @stuff: [...] ### Checking key: $key # print, plus asserts $key is truthy ### Processing |=| ... for @stuff ``` Lines beginning `###` are rewritten into prints (and, with special syntax, progress bars and assertions). The module is a source filter — it rewrites the program text before compile — so reported line numbers can drift by one. When not imported, the `###` comments compile to nothing. The drift is the price of not cluttering the code with explicit `warn` lines. ## Taint and `$&` — incidental costs to know about Referencing `$&`, `` $` ``, or `$'` **anywhere** in the program forces perl to maintain these match-context globals for **every** regex match in the entire program, including matches inside loaded modules. Historically this imposed a noticeable slowdown; on modern perls the cost is smaller but non-zero, and the rule still applies: do not touch them if you can avoid it. Use `@-` / `@+` (match position arrays), named captures, or the `/p` modifier with `${^MATCH}`, `${^PREMATCH}`, `${^POSTMATCH}` instead. The `/p` variants impose the cost only for matches that opt in. ## Timestamped logging Hand-rolled `warn` with `localtime` is adequate for one-off investigations: ```perl use Time::HiRes qw(gettimeofday); sub t { my ($s, $us) = gettimeofday; sprintf "%d.%06d", $s, $us } warn sprintf "[%s] state=%s\n", t(), $state; ``` For anything more structured, use a logging framework rather than rolling your own: - `Log::Any` — library-safe; emit logs without picking a backend. Pair with an adapter (`Log::Any::Adapter::Log4perl`, `::Stdout`, `::Syslog`) in the application. - `Log::Log4perl` — category / level / appender / layout; config file-driven. - `Log::Dispatch` — lower-level router; often used under `Log::Any`. Standard levels, lowest to highest: `TRACE`, `DEBUG`, `INFO`, `WARN`, `ERROR`, `FATAL`. Gate `TRACE` / `DEBUG` behind an env flag in production. ## Find out more - [exceptions](exceptions) — `die`, `$@`, `$SIG{__DIE__}`, typed exception classes, the modern `try`/`catch`. - [interactive-debugger](interactive-debugger) — when inserting prints costs more than launching `perl -d`. - [tracing](tracing) — full-program line-by-line trace without editing the source.