NAME
Devel::Optic - JSON::Pointer meets PadWalker
SYNOPSIS
use Devel::Optic;
my $optic = Devel::Optic->new(max_size => 100);
my $foo = { bar => ['baz', 'blorg', { clang => 'pop' }] };
# 'pop'
$optic->inspect('$foo/bar/-1/clang');
# 'HASH: { bar => ARRAY ...} (1 total keys / 738 bytes). Exceeds viewing size (100 bytes)"
$optic->inspect('$foo');
DESCRIPTION
Devel::Optic is a borescope for Perl programs.
It provides a basic JSON::Pointer-ish path syntax (a 'lens') for extracting bits of complex data structures from a Perl scope based on the variable name. This is intended for use by debuggers or similar introspection/observability tools where the consuming audience is a human troubleshooting a system.
If the data structure selected by the lens is too big, it will summarize the selected data structure into a short, human-readable message. No attempt is made to make the summary machine-readable: it should be immediately passed to a structured logging pipeline.
It takes a caller uplevel and a JSON::Pointer-style 'lens', and returns the variable or summary of a variable found by that lens for the scope of that caller level.
METHODS
new
my $o = Devel::Optic->new(%options);
%options may be empty, or contain any of the following keys:
-
uplevelWhich Perl scope to view. Default: 1 (scope that
Devel::Opticis called from) -
max_sizeMax size, in bytes, of a datastructure that can be viewed without summarization. Default: 5120.
-
scalar_truncation_sizeSize, in bytes, that scalar values are truncated to for viewing. Default: 512.
-
scalar_sample_sizeSize, in bytes, that scalar children of a summarized data structure are trimmed to for inclusion in the summary. Default: 64.
-
ref_key_sample_countNumber of keys/indices to display when summarizing a hash or arrayref. Default: 4.
inspect
my $stuff = { foo => ['a', 'b', 'c'] };
my $o = Devel::Optic->new;
# 'a'
$o->inspect('$stuff/foo/0');
This is the primary method. Given a lens, It will either return the requested data structure, or, if it is too big, return a summary of the data structure found at that path.
fit_to_view
my $some_variable = ['a', 'b', { foo => 'bar' }, [ 'blorg' ] ];
my $tiny = Devel::Optic->new(max_size => 1); # small to force summarization
# "ARRAY: [ 'a', 'b', HASH, ARRAY ]"
$tiny->fit_to_view($some_variable);
my $normal = Devel::Optic->new();
# ['a', 'b', { foo => 'bar' }, [ 'blorg' ] ]
$normal->fit_to_view($some_variable);
This method takes a Perl object/data structure and either returns it unchanged, or produces a 'squished' summary of that object/data structure. This summary makes no attempt to be comprehensive: its goal is to maximally aid human troubleshooting efforts, including efforts to refine a previous invocation of Devel::Optic with a more specific lens.
full_picture
This method takes a 'lens' and uses it to extract a data structure from the
Devel::Optic's uplevel. If the lens points to a variable that does not
exist, Devel::Optic will croak.
LENS SYNTAX
Devel::Optic uses a very basic JSON::Pointer style path syntax called a 'lens'.
A lens always starts with a variable name in the scope being picked,
and uses / to indicate deeper access to that variable. At each level, the
value should be a key or index that can be used to navigate deeper or identify
the target data.
For example, a lens like this:
%my_cool_hash/a/1/needle
Traversing a scope like this:
my %my_cool_hash = (
a => ["blub", { needle => "find me!", some_other_key => "blorb" }],
b => "frobnicate"
);
Will return the value:
"find me!"
A less selective lens on the same data structure:
%my_cool_hash/a
Will return that branch of the tree:
["blub", { needle => "find me!", some_other_key => "blorb" }]
Other syntactic examples:
$hash_ref/a/0/3/blorg
@array/0/foo
$array_ref/0/foo
$scalar
LENS SYNTAX ALTNERATIVES
The 'lens' syntax attempts to provide a reasonable amount of power for navigating Perl data structures without risking the stability of the system under inspection.
In other words, while eval '$my_cool_hash{a}->[1]->{needle}' would
be a much more powerful solution to the problem of navigating Perl data
structures, it opens up all the cans of worms at once.
I'm open to exploring richer syntax in this area as long as it is aligned with the following goals:
-
Simple query model
As a debugging tool, you have enough on your brain just debugging your system. Second-guessing your query syntax when you get unexpected results is a major distraction and leads to loss of trust in the tool (I'm looking at you, ElasticSearch).
-
O(1), not O(n) (or worse)
I'd like to avoid globs or matching syntax that might end up iterating over unbounded chunks of a data structure. Traversing a small, fixed number of keys in 'parallel' sounds like a sane extension, but anything which requires iterating over the entire set of hash keys or array indicies is likely to surprise when debugging systems with unexpectedly large data structures.