App-JsonLogUtils
view release on metacpan or search on metacpan
#!perl
# ABSTRACT: an interactive shell for monitoring JSON log files
# PODNAME: jshell
use strict;
use warnings;
$0 = 'jshell';
use Getopt::Long;
use Pod::Usage;
use List::Util qw(max);
use Term::SimpleColor;
use Term::SimpleColor qw(:background);
use Term::ReadLine;
use Iterator::Simple qw(iterator igrep imap);
use App::JsonLogUtils qw(lines tail json_log);
#-------------------------------------------------------------------------------
# Parse command line options
#-------------------------------------------------------------------------------
my $help = 0;
my $path;
GetOptions(
'help' => \$help,
'path=s' => \$path,
) or pod2usage(2);
do{ pod2usage 1; exit 0; } if $help;
#-------------------------------------------------------------------------------
# Set up global environment
#-------------------------------------------------------------------------------
my $default_prompt = green . '$ ' . default ;
my $prompt = $default_prompt;
my $term = Term::ReadLine->new('jshell');
my (@fields, %match, %skip);
#-------------------------------------------------------------------------------
# Utilities
#-------------------------------------------------------------------------------
sub out { print { $term->OUT } @_, default, "\n" }
sub set_prompt { $prompt = shift || $default_prompt }
#-------------------------------------------------------------------------------
# Iterators
#-------------------------------------------------------------------------------
sub filtered {
my $json = shift;
return igrep{
my ($obj, $line) = @$_;
foreach my $key (keys %match) {
foreach my $pattern (@{ $match{$key} }) {
return unless ($obj->{$key} || '') =~ /$pattern/;
}
}
foreach my $key (keys %skip) {
foreach my $pattern (@{ $skip{$key} }) {
return unless ($obj->{$key} || '') !~ /$pattern/;
}
}
return 1;
} $json;
}
sub formatted {
my $filtered = shift;
imap{
my $obj = $_->[0];
my @keys = @fields ? @fields : sort keys %$obj;
my $len = max map{ length $_ } @keys;
join "\n", map{
sprintf("%s%s[%${len}s]%s%s %s", bg_green, black, $_, bg_default, default, $obj->{$_} || '')
} @keys;
} $filtered;
}
#-------------------------------------------------------------------------------
# Commands
#-------------------------------------------------------------------------------
sub cmd_help {
out green, 'h[elp] Display commands';
out green, 'q[uit] Exits the program';
out green, '[p]ath Sets the default file path for cat and tail';
out green, 'f[ields] field1 [field2 ...] Set the JSON object fields to display';
out green, 'g[rep] field pattern Set a required match pattern for a field';
out green, '[grep]v field pattern Set a forbidden match pattern for a field';
out green, '[i]nfo Display selected fields and patterns';
out green, '[r]eset Interactively reset fields and patterns';
out green, '[c]at Prints all matched lines to the terminal';
out green, '[t]ail Prints all matched lines to the terminal, continuing as the file is appended. Control-c returns to the shell.';
out;
out green, 'All patterns are case sensitive, except when using embedded match modifiers: (?i)..(?-i).';
out;
}
sub cmd_quit {
sub cmd_info {
cmd_fields;
cmd_grep;
cmd_grepv;
}
sub cmd_reset {
my $reset_fields = $term->readline("Clear fields? [y/n] ");
my $reset_match = $term->readline("Clear grep? [y/n] ");
my $reset_skip = $term->readline("Clear grepv? [y/n] ");
undef @fields if $reset_fields =~ /^y(es)?$/i;
undef %match if $reset_match =~ /^y(es)?$/i;
undef %skip if $reset_skip =~ /^y(es)?$/i;
cmd_info;
}
sub cmd_cat {
my $file = shift || $path;
$path = $file if defined $path;
my $entries = formatted filtered json_log lines $file;
while (my $entry = <$entries>) {
out $entry;
out;
}
}
sub cmd_tail {
my $file = shift || $path;
$path = $file if defined $path;
my $entries = formatted filtered json_log tail $file;
while (my $entry = <$entries>) {
out $entry;
out;
}
}
#-------------------------------------------------------------------------------
# Aliases
#-------------------------------------------------------------------------------
sub cmd_c { goto \&cmd_cat }
sub cmd_p { goto \&cmd_path }
sub cmd_f { goto \&cmd_fields }
sub cmd_g { goto \&cmd_grep }
sub cmd_h { goto \&cmd_help }
sub cmd_i { goto \&cmd_info }
sub cmd_q { goto \&cmd_quit }
sub cmd_r { goto \&cmd_reset }
sub cmd_t { goto \&cmd_tail }
sub cmd_v { goto \&cmd_grepv }
#-------------------------------------------------------------------------------
# Main loop
#-------------------------------------------------------------------------------
while (defined(my $input = $term->readline($prompt))) {
next unless $input;
chomp $input;
if ($input eq '!!') {
$input = $term->previous_history;
out $input;
}
my ($cmd, @args) = split /\s+/, $input;
if (__PACKAGE__->can("cmd_$cmd")) {
$term->addhistory($input);
__PACKAGE__->can("cmd_$cmd")->(@args);
}
else {
out red, "Invalid command; type help for a list of commands.";
}
}
__END__
=pod
=encoding UTF-8
=head1 NAME
jshell - an interactive shell for monitoring JSON log files
=head1 VERSION
version 0.03
=head1 SYNOPSIS
jshell [--path /path/to/log/file]
=head1 DESCRIPTION
Opens a shell to interact with JSON-formatted log files.
=head1 OPTIONS
=head2 path
Optionally sets the default path to work with.
=head1 COMMANDS
=head2 help | h
Displays commands and their descriptions.
=head2 quit | q
Exits the program.
=head2 path | p
Sets or displays the default path.
( run in 1.654 second using v1.01-cache-2.11-cpan-6aa56a78535 )