[Rt-commit] rt branch 5.0/add-scrip-logging created. rt-5.0.4-64-g56bfc92e6d
BPS Git Server
git at git.bestpractical.com
Mon Jul 31 22:58:00 UTC 2023
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "rt".
The branch, 5.0/add-scrip-logging has been created
at 56bfc92e6d4bda9b08c7fc21b36acf29b2187490 (commit)
- Log -----------------------------------------------------------------
commit 56bfc92e6d4bda9b08c7fc21b36acf29b2187490
Author: Brad Embree <brad at bestpractical.com>
Date: Mon Jul 31 14:59:08 2023 -0700
Add tests
diff --git a/t/web/scrips.t b/t/web/scrips.t
index 1e9ee4e3fc..fff74c5a89 100644
--- a/t/web/scrips.t
+++ b/t/web/scrips.t
@@ -2,6 +2,7 @@ use strict;
use warnings;
use RT::Test tests => undef;
+use Test::Warn;
RT->Config->Set( UseTransactionBatch => 1 );
@@ -296,4 +297,108 @@ note "apply scrip in different stage to different queues";
is scalar @matches, 1, 'scrip mentioned only once';
}
+note "test scrip logging";
+{
+ my $logdir = RT->Config->Get('LogDir') || File::Spec->catdir( $RT::VarPath, 'log' );
+ $logdir = File::Spec->catdir( $logdir, 'scrips' );
+
+ my %test_scrips = (
+ 'No Errors' => [ 'return 1;', 'return 1;', 'return 1;' ],
+ 'IsApplicable Error' => [ 'return $undefined;', 'return 1;', 'return 1;' ],
+ 'Prepare Error' => [ 'return 1;', 'return $undefined;', 'return 1;' ],
+ 'Commit Error' => [ 'return 1;', 'return 1;', 'return $undefined;' ],
+ );
+ my %test_scrip_logfile_should_exist = (
+ 'No Errors' => { IsApplicable => 0, Prepare => 0, Commit => 0, },
+ 'IsApplicable Error' => { IsApplicable => 1, Prepare => 0, Commit => 0, },
+ 'Prepare Error' => { IsApplicable => 0, Prepare => 1, Commit => 0, },
+ 'Commit Error' => { IsApplicable => 0, Prepare => 0, Commit => 1, },
+ );
+
+ my %id_for_scrip;
+ foreach my $test_scrip ( sort keys %test_scrips ) {
+ diag "Create Scrip (Test Scrip Logging - $test_scrip)" if $ENV{TEST_VERBOSE};
+ $m->follow_link_ok({id => 'admin-global-scrips-create'});
+ $m->form_name('CreateScrip');
+ $m->set_fields(
+ 'Description' => "Test Scrip Logging - $test_scrip",
+ 'ScripCondition' => 'User Defined',
+ 'ScripAction' => 'User Defined',
+ 'Template' => 'Blank',
+ 'CustomIsApplicableCode' => $test_scrips{$test_scrip}->[0],
+ 'CustomPrepareCode' => $test_scrips{$test_scrip}->[1],
+ 'CustomCommitCode' => $test_scrips{$test_scrip}->[2],
+ );
+ $m->click('Create');
+ $m->content_like(qr{Scrip Created});
+
+ my ($sid) = ($m->content =~ /Modify scrip #(\d+)/);
+ ok $sid, "found scrip id on the page";
+
+ $id_for_scrip{$test_scrip} = $sid;
+ }
+
+ # creating a ticket should fire off all test scrips
+ diag "Create Ticket (Test Scrip Logging No Config)" if $ENV{TEST_VERBOSE};
+ warnings_like {
+ RT::Test->create_ticket(
+ Subject => 'Test Scrip Logging',
+ Content => 'stuff',
+ Queue => 1,
+ );
+ } [ qr/Global symbol .* requires explicit package name/,
+ qr/Global symbol .* requires explicit package name/,
+ qr/Global symbol .* requires explicit package name/,
+ qr/Global symbol .* requires explicit package name/,
+ qr/Global symbol .* requires explicit package name/,
+ qr/Global symbol .* requires explicit package name/,
+ ];
+
+ # without any config specified there should be no log files
+ foreach my $test_scrip ( sort keys %id_for_scrip ) {
+ foreach my $mode ( qw( IsApplicable Prepare Commit ) ) {
+ my $filename = 'scrip-' . $id_for_scrip{$test_scrip} . '-' . $mode . '.log';
+ my $fullpath = File::Spec->catfile( $logdir, $filename );
+
+ ok ! -e $fullpath, "Scrip log file '$filename' should not exist";
+ }
+ }
+
+ # now set config and create another ticket
+ # need to stop server, change config, restart server
+ # to avoid warning about changing config with running server
+ RT::Test->stop_server;
+ RT->Config->Set( LogScripsForUser => { root => 'warn', RT_System => 'warn' } );
+ ( $baseurl, $m ) = RT::Test->started_ok;
+ ok( $m->login(), 'logged in' );
+
+ diag "Create Ticket (Test Scrip Logging With Config)" if $ENV{TEST_VERBOSE};
+ warnings_like {
+ RT::Test->create_ticket(
+ Subject => 'Test Scrip Logging',
+ Content => 'stuff',
+ Queue => 1,
+ );
+ } [ qr/Global symbol .* requires explicit package name/,
+ qr/Global symbol .* requires explicit package name/,
+ qr/Global symbol .* requires explicit package name/,
+ qr/Global symbol .* requires explicit package name/,
+ qr/Global symbol .* requires explicit package name/,
+ qr/Global symbol .* requires explicit package name/,
+ ];
+
+ foreach my $test_scrip ( sort keys %id_for_scrip ) {
+ foreach my $mode ( qw( IsApplicable Prepare Commit ) ) {
+ my $filename = 'scrip-' . $id_for_scrip{$test_scrip} . '-' . $mode . '.log';
+ my $fullpath = File::Spec->catfile( $logdir, $filename );
+
+ if ( $test_scrip_logfile_should_exist{$test_scrip}->{$mode} ) {
+ ok -e $fullpath, "Scrip log file '$filename' should exist";
+ } else {
+ ok ! -e $fullpath, "Scrip log file '$filename' should not exist";
+ }
+ }
+ }
+}
+
done_testing;
commit e1c1b2ab87b3596bf9a1621a79d177296c4f7e53
Author: Brad Embree <brad at bestpractical.com>
Date: Mon Jul 31 14:58:22 2023 -0700
Show Scrip errors for UserDefined code
diff --git a/share/html/Admin/Scrips/Elements/EditCustomCode b/share/html/Admin/Scrips/Elements/EditCustomCode
index 201a8c292f..345a67dbd9 100644
--- a/share/html/Admin/Scrips/Elements/EditCustomCode
+++ b/share/html/Admin/Scrips/Elements/EditCustomCode
@@ -65,6 +65,18 @@
<textarea spellcheck="false" cols="80" class="form-control" rows="<% $lines %>" name="<% $method %>"><% $code %></textarea>
</div>
</div>
+
+% if ( $errors{$method} ) {
+<div class="form-row">
+ <div class="label col-2 labeltop">
+ <span style="color:red"><% loc('Logging') %></span>:
+ </div>
+ <div class="value col-9">
+ <textarea spellcheck="false" cols="80" rows="5" class="form-control" readonly><% $errors{$method} %></textarea>
+ </div>
+</div>
+% }
+
% }
</&>
@@ -79,4 +91,34 @@ my @list = (
);
my $min_lines = 10;
+
+my %errors = (
+ 'CustomIsApplicableCode' => '',
+ 'CustomPrepareCode' => '',
+ 'CustomCommitCode' => '',
+);
+
+if ( $Scrip->id ) {
+ my @stages = ();
+ if ( $Scrip->ConditionObj->ExecModule eq 'UserDefined' ) {
+ push @stages, 'IsApplicable';
+ }
+ if ( $Scrip->ActionObj->ExecModule eq 'UserDefined' ) {
+ push @stages, 'Prepare', 'Commit';
+ }
+
+ my $logdir = RT->Config->Get('LogDir') || File::Spec->catdir( $RT::VarPath, 'log' );
+ $logdir = File::Spec->catdir( $logdir, 'scrips' );
+ foreach my $stage ( @stages ) {
+ my $filename = File::Spec->catfile( $logdir, 'scrip-' . $Scrip->id . '-' . $stage . '.log' );
+ if ( -e $filename ) {
+ if ( -s $filename ) {
+ local $/;
+ open ( my $f, '<:encoding(UTF-8)', $filename )
+ or die "Cannot open initialdata file '$filename' for read: $@";
+ $errors{ 'Custom' . $stage . 'Code' } = <$f>;
+ }
+ }
+ }
+}
</%INIT>
commit e4d083668d7c6b94e3c6d2c6875c537714aa7d05
Author: Brad Embree <brad at bestpractical.com>
Date: Mon Jul 31 14:56:25 2023 -0700
Add Logging tab to Scrip Admin menu
diff --git a/lib/RT/Interface/Web/MenuBuilder.pm b/lib/RT/Interface/Web/MenuBuilder.pm
index 696d21b9af..f7bbc12d80 100644
--- a/lib/RT/Interface/Web/MenuBuilder.pm
+++ b/lib/RT/Interface/Web/MenuBuilder.pm
@@ -1567,6 +1567,7 @@ sub _BuildAdminMenu {
$page->child( basics => title => loc('Basics') => path => "/Admin/Scrips/Modify.html?id=" . $id . $from_query_param );
$page->child( 'applies-to' => title => loc('Applies to'), path => "/Admin/Scrips/Objects.html?id=" . $id . $from_query_param );
+ $page->child( 'logging' => title => loc('Logging'), path => "/Admin/Scrips/Logging.html?id=" . $id . $from_query_param );
}
elsif ( $request_path =~ m{^/Admin/Scrips/(index\.html)?$} ) {
HTML::Mason::Commands::PageMenu->child( select => title => loc('Select') => path => "/Admin/Scrips/" );
commit f9b9936b7fa26ba28cd34ad5be6577202e301ee8
Author: Brad Embree <brad at bestpractical.com>
Date: Mon Jul 31 14:55:57 2023 -0700
Add Scrip Logging page
diff --git a/share/html/Admin/Scrips/Logging.html b/share/html/Admin/Scrips/Logging.html
new file mode 100644
index 0000000000..0e261a5df4
--- /dev/null
+++ b/share/html/Admin/Scrips/Logging.html
@@ -0,0 +1,113 @@
+%# BEGIN BPS TAGGED BLOCK {{{
+%#
+%# COPYRIGHT:
+%#
+%# This software is Copyright (c) 1996-2023 Best Practical Solutions, LLC
+%# <sales at bestpractical.com>
+%#
+%# (Except where explicitly superseded by other copyright notices)
+%#
+%#
+%# LICENSE:
+%#
+%# This work is made available to you under the terms of Version 2 of
+%# the GNU General Public License. A copy of that license should have
+%# been provided with this software, but in any event can be snarfed
+%# from www.gnu.org.
+%#
+%# This work is distributed in the hope that it will be useful, but
+%# WITHOUT ANY WARRANTY; without even the implied warranty of
+%# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%# General Public License for more details.
+%#
+%# You should have received a copy of the GNU General Public License
+%# along with this program; if not, write to the Free Software
+%# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+%# 02110-1301 or visit their web page on the internet at
+%# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
+%#
+%#
+%# CONTRIBUTION SUBMISSION POLICY:
+%#
+%# (The following paragraph is not intended to limit the rights granted
+%# to you to modify and distribute this software under the terms of
+%# the GNU General Public License and is only of importance to you if
+%# you choose to contribute your changes and enhancements to the
+%# community by submitting them to Best Practical Solutions, LLC.)
+%#
+%# By intentionally submitting any modifications, corrections or
+%# derivatives to this work, or any other work intended for use with
+%# Request Tracker, to Best Practical Solutions, LLC, you confirm that
+%# you are the copyright holder for those contributions and you grant
+%# Best Practical Solutions, LLC a nonexclusive, worldwide, irrevocable,
+%# royalty-free, perpetual, license to use, copy, create derivative
+%# works based on those contributions, and sublicense and distribute
+%# those contributions and any derivatives thereof.
+%#
+%# END BPS TAGGED BLOCK }}}
+<& /Admin/Elements/Header, Title => loc("Logging for scrip #[_1]", $id) &>
+<& /Elements/Tabs &>
+
+<div class="mx-auto max-width-xl">
+
+<&| /Widgets/TitleBox, title => loc('Logging') &>
+
+<div class="form-row">
+ <div class="label col-3">
+ <span class="prev-icon-helper"><% loc('Condition') %>:</span>
+ </div>
+ <div class="value col-9">
+ <textarea cols="15" rows="5" name="Condition" class="form-control" readonly><% $errors{IsApplicable} %></textarea>
+ </div>
+</div>
+<div class="form-row">
+ <div class="label col-3">
+ <span class="prev-icon-helper"><% loc('Action preparation')%>:</span>
+ </div>
+ <div class="value col-9">
+ <textarea cols="15" rows="5" name="Prepare" class="form-control" readonly><% $errors{Prepare} %></textarea>
+ </div>
+</div>
+<div class="form-row">
+ <div class="label col-3">
+ <span class="prev-icon-helper"><% loc('Action commit') %>:</span>
+ </div>
+ <div class="value col-9">
+ <textarea cols="15" rows="5" name="Commit" class="form-control" readonly><% $errors{Commit} %></textarea>
+ </div>
+</div>
+
+</div>
+
+</&>
+
+<%ARGS>
+$id => undef
+$From => undef
+</%ARGS>
+<%INIT>
+my $scrip = RT::Scrip->new( $session{'CurrentUser'} );
+$scrip->Load( $id );
+Abort(loc("Couldn't load scrip #[_1]", $id))
+ unless $scrip->id;
+
+my %errors = (
+ 'IsApplicable' => '',
+ 'Prepare' => '',
+ 'Commit' => '',
+);
+
+my $logdir = RT->Config->Get('LogDir') || File::Spec->catdir( $RT::VarPath, 'log' );
+$logdir = File::Spec->catdir( $logdir, 'scrips' );
+foreach my $stage ( qw( IsApplicable Prepare Commit ) ) {
+ my $filename = File::Spec->catfile( $logdir, 'scrip-' . $scrip->id . '-' . $stage . '.log' );
+ if ( -e $filename ) {
+ if ( -s $filename ) {
+ local $/;
+ open ( my $f, '<:encoding(UTF-8)', $filename )
+ or die "Cannot open initialdata file '$filename' for read: $@";
+ $errors{$stage} = <$f>;
+ }
+ }
+}
+</%INIT>
commit 294b3487d43a3f93ac831f43ca4901731eb51a2f
Author: Brad Embree <brad at bestpractical.com>
Date: Mon Jul 31 14:54:39 2023 -0700
Add HasLogs to Scrip AdminSearchResultFormat
diff --git a/etc/RT_Config.pm.in b/etc/RT_Config.pm.in
index fb805ba43a..d045d8993e 100644
--- a/etc/RT_Config.pm.in
+++ b/etc/RT_Config.pm.in
@@ -3977,7 +3977,7 @@ Set(%AdminSearchResultFormat,
Scrips =>
q{'<a href="__WebPath__/Admin/Scrips/Modify.html?id=__id____From__">__id__</a>/TITLE:#'}
.q{,'<a href="__WebPath__/Admin/Scrips/Modify.html?id=__id____From__">__Description__</a>/TITLE:Description'}
- .q{,__Condition__, __Action__, __Template__, __Disabled__},
+ .q{,__Condition__, __Action__, __Template__, __Disabled__,__HasLogs__},
Templates =>
q{'<a href="__WebPath__/__WebRequestPathDir__/Template.html?Queue=__QueueId__&Template=__id__">__id__</a>/TITLE:#'}
commit e7f5ef3869dc27a9bc3cba014e39305fd10f6541
Author: Brad Embree <brad at bestpractical.com>
Date: Mon Jul 31 14:50:01 2023 -0700
Add HasLogs column for Scrips
diff --git a/share/html/Elements/RT__Scrip/ColumnMap b/share/html/Elements/RT__Scrip/ColumnMap
index 1edac2a6d9..4e86be2748 100644
--- a/share/html/Elements/RT__Scrip/ColumnMap
+++ b/share/html/Elements/RT__Scrip/ColumnMap
@@ -181,6 +181,21 @@ my $COLUMN_MAP = {
return $_[0]->loc( $os->FriendlyStage );
},
},
+ HasLogs => {
+ title => 'Logging', # loc
+ value => sub {
+ my $logdir = RT->Config->Get('LogDir') || File::Spec->catdir( $RT::VarPath, 'log' );
+ $logdir = File::Spec->catdir( $logdir, 'scrips' );
+ foreach my $stage ( qw( IsApplicable Prepare Commit ) ) {
+ my $filename = File::Spec->catfile( $logdir, 'scrip-' . $_[0]->id . '-' . $stage . '.log' );
+ if ( -e $filename && -s $filename ) {
+ my $return = '<span style="color:red">' . $_[0]->loc('Has Log') . '</span>';
+ return \$return;
+ }
+ }
+ return '';
+ },
+ },
};
</%ONCE>
commit b5f3ac0bf9a0ea6680cb6e679d6de164bd22d340
Author: Brad Embree <brad at bestpractical.com>
Date: Thu Jul 13 19:16:27 2023 -0700
Add logging for all scrip stages
diff --git a/lib/RT/Scrip.pm b/lib/RT/Scrip.pm
index ed56cafc61..f3042379c6 100644
--- a/lib/RT/Scrip.pm
+++ b/lib/RT/Scrip.pm
@@ -584,6 +584,9 @@ sub IsApplicable {
return (undef);
}
my $ConditionObj = $self->ConditionObj;
+
+ $self->_AddFileLogger('IsApplicable');
+
foreach my $TransactionObj ( @Transactions ) {
# in TxnBatch stage we can select scrips that are not applicable to all txns
my $txn_type = $TransactionObj->Type;
@@ -602,6 +605,7 @@ sub IsApplicable {
}
}
};
+ $self->_RemoveFileLogger('IsApplicable');
if ($@) {
$RT::Logger->error( "Scrip IsApplicable " . $self->Id . " died. - " . $@ );
@@ -635,8 +639,12 @@ sub Prepare {
TemplateObj => $self->TemplateObj( $args{'TicketObj'}->Queue ),
);
+ $self->_AddFileLogger('Prepare');
+
$return = $self->ActionObj->Prepare();
};
+ $self->_RemoveFileLogger('Prepare');
+
if ($@) {
$RT::Logger->error( "Scrip Prepare " . $self->Id . " died. - " . $@ );
return (undef);
@@ -662,8 +670,11 @@ sub Commit {
my $return;
eval {
+ $self->_AddFileLogger('Commit');
+
$return = $self->ActionObj->Commit();
};
+ $self->_RemoveFileLogger('Commit');
#Searchbuilder caching isn't perfectly coherent. got to reload the ticket object, since it
# may have changed
@@ -680,9 +691,32 @@ sub Commit {
return ($return);
}
+sub _AddFileLogger {
+ my $self = shift;
+ my $mode = shift;
+ my $config = RT->Config->Get('LogScripsForUser');
+ my $current_user = $HTML::Mason::Commands::session{CurrentUser} || $self->CurrentUser;
+ return unless $config;
+ return unless $current_user;
+ return unless $config->{ $current_user->Name };
+ RT::AddFileLogger(
+ filename => 'scrip-' . $self->id . "-$mode.log",
+ log_level => $config->{ $current_user->Name },
+ );
+}
+
+sub _RemoveFileLogger {
+ my $self = shift;
+ my $mode = shift;
+
+ my $current_user = $HTML::Mason::Commands::session{CurrentUser} || $self->CurrentUser;
+ my $final_log = "\nLog created on " . gmtime(time) . " for " . $current_user->Name;
+
+ RT::RemoveFileLogger( 'scrip-' . $self->id . "-$mode.log", $final_log );
+}
# does an acl check and then passes off the call
sub _Set {
commit 8a4a6fbe2334d901d9647f693ba11de5cc709729
Author: Brad Embree <brad at bestpractical.com>
Date: Mon Jul 31 14:49:00 2023 -0700
Add LogScripsForUser config option
diff --git a/etc/RT_Config.pm.in b/etc/RT_Config.pm.in
index adcd78594c..fb805ba43a 100644
--- a/etc/RT_Config.pm.in
+++ b/etc/RT_Config.pm.in
@@ -378,6 +378,22 @@ See the L<Log::Dispatch::Syslog> documentation for more information.
Set(@LogToSyslogConf, ());
+=item C<$LogScripsForUser>
+
+Logging specifically for Scrips, enabled on a per user basis.
+
+If set, any Scrip that runs has logging enabled at the log level
+specified and if there is any log content it is displayed in the Scrip
+Admin interface.
+
+Map a User's Name to a log level to use for Scrip logging:
+
+ Set($LogScripsForUser, { User => 'warn' });
+
+=cut
+
+Set($LogScripsForUser, {});
+
=back
commit 98b59574b1b05d65873052e286da32eba4cfcda6
Author: Brad Embree <brad at bestpractical.com>
Date: Thu Jul 13 19:15:32 2023 -0700
Add methods for adding and removing a logger
diff --git a/lib/RT.pm b/lib/RT.pm
index ea47927889..329e800f5a 100644
--- a/lib/RT.pm
+++ b/lib/RT.pm
@@ -412,6 +412,90 @@ sub InitSignalHandlers {
};
}
+sub AddFileLogger {
+ my %args = (
+ log_level => 'warn',
+ @_
+ );
+
+ return unless $args{filename};
+
+ # return if there is a already a logger with this name
+ return if $RT::Logger->output( $args{filename} );
+
+ my $logdir = RT->Config->Get('LogDir') || File::Spec->catdir( $VarPath, 'log' );
+ $logdir = File::Spec->catdir( $logdir, 'scrips' );
+ my $filename = File::Spec->catfile( $logdir, $args{filename} );
+
+ unless ( -e $logdir ) {
+ mkdir $logdir;
+ }
+ unless ( -d $logdir && -w $logdir ) {
+ # localizing here would be hard when we don't have a current user yet
+ RT->Logger->error("Log dir '$logdir' is not writeable.");
+ return;
+ }
+
+ my $simple_cb = sub {
+ # if this code throw any warning we can get segfault
+ no warnings;
+ my %p = @_;
+
+ # skip Log::* stack frames
+ my $frame = 0;
+ $frame++ while caller($frame) && caller($frame) =~ /^Log::/;
+ my ($package, $filename, $line) = caller($frame);
+
+ # Encode to bytes, so we don't send wide characters
+ $p{message} = Encode::encode("UTF-8", $p{message});
+
+ $p{'message'} =~ s/(?:\r*\n)+$//;
+ return "[$$] [". gmtime(time) ."] [". $p{'level'} ."]: "
+ . $p{'message'} ." ($filename:$line)\n";
+ };
+
+ require Log::Dispatch::File;
+ $RT::Logger->add(
+ Log::Dispatch::File->new(
+ name => $args{filename},
+ min_level => $args{log_level},
+ filename => $filename,
+ mode => 'write',
+ callbacks => [ $simple_cb ],
+ )
+ );
+
+ return 1;
+}
+
+sub RemoveFileLogger {
+ my $filename = shift;
+ my $final_log = shift;
+
+ return unless $filename;
+
+ # return if there is not a logger with this name
+ return unless $RT::Logger->output($filename);
+
+ $RT::Logger->remove($filename);
+
+ # if the log file is empty delete it
+ my $logdir = RT->Config->Get('LogDir') || File::Spec->catdir( $VarPath, 'log' );
+ $logdir = File::Spec->catdir( $logdir, 'scrips' );
+ $filename = File::Spec->catfile( $logdir, $filename );
+
+ if ( ( -e $filename ) && ( -s $filename == 0 ) ) {
+ unlink $filename;
+ } elsif ( $final_log ) {
+ # add a final message with log details
+ if ( open my $fh, '>>', $filename ) {
+ print $fh $final_log;
+ close $fh;
+ } else {
+ RT->Logger->error("Cannot write to '$filename': $!");
+ }
+ }
+}
sub CheckPerlRequirements {
eval {require 5.010_001};
-----------------------------------------------------------------------
hooks/post-receive
--
rt
More information about the rt-commit
mailing list