package SLUB::LZA::Rosetta::TA::Command::log; use strict; use warnings; use feature qw(say); use Regexp::Optimizer; use DateTime; use DateTime::Format::DateParse; use SLUB::LZA::Rosetta::TA -command; sub abstract {"grep server log of Rosetta based Archival Information System";} my $description=<<"DESCR"; Searches logfiles of Rosetta-based AIS Examples: * What are the error messages in last 24 hours? $0 log --level error --last-24h * What are error and warning messages between 2022-01-01 and 2022-02-01? $0 log --level error --level warning --fromdate 2022-01-01 --todate 2021-02-01 * Are there lines with regex "ma[tc]?h"? $0 log --match "ma[tc]?h" * Give me a trace of specific sip $0 log --trace-sip sip293144 DESCR sub description { "$description" } sub opt_spec { return( ["verbose|v" => "enable verbose output"], ["outputfilter" => hidden => {one_of => [ ["colorize|c" => "colorize output"], ["csv" => "use csv output"], ]}], ["last-24h" => "search within last 24h"], ["fromdate=s" => "search starting with date"], ["todate=s" => "search ending with date"], ["level=s@" => "levels to search for. Levels could be: 'error', 'warn', 'info', 'debug'. You could use multiple levels by repeating"], ["match=s" => "perl regex to search for" => {default=>".*"}], ["trace-sip=s" => "trace a sip with given ID (SIP-ID or Deposit-ID)"], ); } sub validate_date { my $self = shift; my $datestr = shift; return ($datestr =~ m/^20[0123][0-9]-[0-1][0-9]-[0-3][0-9]$/); } sub validate_args { my ($self, $opt, $args) = @_; # no args allowed but options! $self->usage_error("No args allowed") if @$args; if (defined $opt->fromdate and defined $opt->last_24h and $opt->last_24h == 1 ) { $self->usage_error("--last-24h and --fromdate not combinable"); } if (defined $opt->todate and defined $opt->last_24h and $opt->last_24h == 1 ) { $self->usage_error("--last-24h and --todate not combinable"); } # check dates if (defined $opt->fromdate && !$self->validate_date($opt->fromdate)) { $self->usage_error("--fromdate $opt->{fromdate} not a valid date"); } if (defined $opt->todate && !$self->validate_date($opt->todate)) { $self->usage_error("--todate $opt->{todate} not a valid date"); } # TODO: check levels 1; } sub create_regex_last24h { my $dt = DateTime->now; my $todate = $dt->ymd; my $fromdate= $dt->subtract( hours => 24)->ymd; my $rxo = Regexp::Optimizer->new->optimize(qr/$fromdate|$todate/); return $rxo; } sub create_regex_from_to { my $from=shift // "2000-01-01"; my $to=shift // "2059-12-31"; my $dt_from = DateTime::Format::DateParse->parse_datetime("$from"); my $dt_to = DateTime::Format::DateParse->parse_datetime("$to"); my @date_tmo_s; for (my $dt = $dt_from; $dt->epoch() <= $dt_to->epoch; $dt->add(days => 1) ) { push @date_tmo_s, $dt->ymd; } my $date_rx_string = join("|", @date_tmo_s); my $rxo = Regexp::Optimizer->new->optimize(qr/$date_rx_string/); return $rxo; } sub execute { my ($self, $opt, $args) = @_; # create date_rx if provided by CLI my $date_rx=qr/[^ ]*/; if (defined $opt->last_24h and $opt->last_24h == 1) { $date_rx=create_regex_last24h(); } else { $date_rx=create_regex_from_to($opt->fromdate, $opt->todate); } # create level_rx if multiple levels provided by CLI my $level_rx=qr/(DEBUG|INFO|WARN|ERROR)/; if (defined $opt->level) { my $rx_string = join("|", map {uc} @{ $opt->level }); $level_rx = Regexp::Optimizer->new->optimize(qr/$rx_string/); } my $match_rx=qr{$opt->{match}}; # prepare output filter my $output_filter=sub { $_[0]; }; if (defined $opt->colorize) { $output_filter = sub { colorize($_[0], $opt, $match_rx); }; } elsif (defined $opt->csv) { $output_filter = sub { csv($_[0], $opt, $match_rx); }; } # prepare trace my $with_trace; if (defined $opt->trace_sip) { $with_trace = $opt->trace_sip; if (defined $opt->colorize) { $output_filter = sub { colorize($_[0], $opt, $match_rx); }; } else { $output_filter=sub { $_[0]; }; } SLUB::LZA::Rosetta::TA::trace_log($with_trace, $opt->colorize, $output_filter); } else { SLUB::LZA::Rosetta::TA::scan_log($date_rx, $level_rx, $match_rx, $output_filter); } } 1;