Skip to content

Instantly share code, notes, and snippets.

@s1037989
Last active December 9, 2024 10:34
Show Gist options
  • Save s1037989/2e513d15ea2855323d17b5b64e5e7810 to your computer and use it in GitHub Desktop.
Save s1037989/2e513d15ea2855323d17b5b64e5e7810 to your computer and use it in GitHub Desktop.
package Mojolicious::Command::start;
use Mojo::Base 'Mojolicious::Command', -signatures;
use Mojo::Util qw(extract_usage getopt);
has description => 'Show versions of available modules';
has usage => sub { shift->extract_usage };
has perlbrew_with => sub {
return undef unless $ENV{PERLBREW_ROOT} && $ENV{PERLBREW_HOME} && $ENV{PERLBREW_PERL};
return join '@', $ENV{PERLBREW_PERL}, $ENV{PERLBREW_LIB};
};
sub run ($self, @args) {
my @exec;
if (my $perlbrew_with = $self->perlbrew_with) {
@exec = ('perlbrew', 'exec', '--with', $perlbrew_with);
}
$ENV{MOJO_LISTEN} = join ',', $self->app->config->{listen}->@* if $self->app->config->{listen};
$ENV{MOJO_REVERSE_PROXY} = join ',', $self->app->config->{proxy} if $self->app->config->{proxy};
_exec(@exec, $self->app->config->{start}, @args) if $self->app->config->{start};
die "missing configuration key 'start'\n";
}
sub _exec {
$_ = join ' ', @_;
s/\$0/$0/g;
warn "$_\n";
exec $_;
}
1;
package Mojolicious::Plugin::SmartLog;
use Mojo::Base 'Mojolicious::Plugin', -signatures;
use Mojo::File qw();
use Time::HiRes qw(time);
has level => undef;
has path => undef;
has stderr => 0;
has user => sub { sub ($c) {} };
sub register ($self, $app, $conf) {
# Note: logging to stdout/stderr will not get picked up by journald when run with hypnotoad
# When using hypnotoad, you should log to a file and use journald to monitor the file
$app->config->{log} ||= {};
$self->stderr($ENV{MOJO_LOG_STDERR} || $conf->{stderr} || ($app->config->{log}{stderr} && $app->config->{log}{stderr} =~ /^\s*(1|y|yes|on|true)\s*$/i));
if ($ENV{LOGS_DIRECTORY} && -d $ENV{LOGS_DIRECTORY}) {
$self->path(Mojo::File::path($ENV{LOGS_DIRECTORY})->child($app->mode . '.log')->touch);
}
elsif (my $path = $conf->{path} || $app->config->{log}{path}) {
$self->path($path)->tap(sub { $_->dirname->make_path });
}
elsif (-d $app->home->child('log')) {
$self->path($app->home->child('log', $app->mode . '.log'));
}
$app->log->path($self->path) if $self->path;
$self->level($ENV{MOJO_LOG_LEVEL} || $conf->{level} || $app->config->{log}{level});
$app->log->level($self->level) if $self->level;
my $time = time;
warn sprintf "[smart_log] %s\n", Mojo::File::path($app->log->path)->realpath if $app->log->path;
$app->log->on(message => sub ($log, $level, @lines) {
# warn Mojo::Util::dumper($log);
if ($log->path && $self->stderr) {
warn (($conf->{log_time} || $app->config->{log}{log_time}) ? $log->format->(time, $level, @lines) : sprintf("[%.3f] [%s] %s\n", time - $time, $level, join " ", @lines));
}
return if grep { /Error handling errors/ } @lines; # don't handle errors if there's an error handling errors
return unless grep { /Raptor (Rainbow|Not Found) Shown/ } @lines; # only handle errors if there's a rainbow or not found shown
# Handle raptor problems, such as sending a special alert or logging to a different file
# $conf->{raptor_handler}->($log, $level, @lines) if $conf->{raptor_handler};
# $self->emit('raptor', $log, $level, @lines);
});
$app->hook(before_render => sub ($c, $args) {
return unless my $template = $args->{template};
my $user = ref $conf->{user} eq 'CODE' ? $conf->{user}->($c) : '-';
if ($template eq 'exception') { # Make sure we are rendering the exception template
return unless my $e = $c->stash->{exception};
return unless my $snapshot = $c->stash->{snapshot};
$c->log->error(sprintf '[%s] [%s] Raptor Rainbow Shown: %s (%s#%s); (%d) %s', $args->{status}, $user, $c->req->url, $snapshot->{controller}, $snapshot->{action}, $e->line->[0], $e);
$self->emit('raptor', $c->log->history->[-1]->@*);
}
elsif ($template eq 'not_found') { # Make sure we are rendering the not_found template
$c->log->warn(sprintf '[%s] [%s] Raptor Not Found Shown: %s', $args->{status}, $user, $c->req->url);
$self->emit('raptor', $c->log->history->[-1]->@*);
}
});
}
1;
package Mojolicious::Plugin::Minion::AutoPerform;
use Mojo::Base 'Mojolicious::Plugin', -signatures;
use Mojo::IOLoop;
sub register ($self, $app, $conf) {
Mojo::IOLoop->recurring($conf->{auto_perform} || 3 => sub {
my $minion = $app->minion;
my $workers = $minion->workers;
return if $workers->total;
my @inactive;
my $queues = $conf->{queues} || ['default'];
$minion->jobs({states => ['inactive'], queues => $queues})->each(sub {push @inactive, $_->{id}});
my $inactive = $#inactive + 1;
return unless $inactive;
if ($conf->{require_worker} // 1) {
$app->log->warn(sprintf 'No registered workers, NOT performing %d jobs (%s) in app', $inactive, join ', ', @inactive);
}
else {
my $queued = $minion->jobs({queues => $queues})->total;
$minion->perform_jobs({queues => $queues});
my $performed = $queued - $minion->jobs({queues => $queues})->total;
$app->log->warn(sprintf 'No registered workers but performed %d jobs in app', $performed) if $performed && $app->mode eq 'production';
}
});
return $self;
}
1;
package Mojolicious::Plugin::AppPaths;
use Mojo::Base 'Mojolicious::Plugin', -signatures;
use Mojo::File qw();
sub register ($self, $app, $conf) {
my $home = $app->home;
$app->helper('paths.app' => sub { $home });
$app->helper('paths.data' => sub { Mojo::File::path($ENV{MOJO_DATA} || $conf->{data} || $home) });
$app->helper('paths.tmp' => sub { Mojo::File::path($conf->{tmp} || '/tmp') });
$app->helper('paths.vartmp' => sub { Mojo::File::path($conf->{vartmp} || '/var/tmp') });
$app->helper('paths.cache' => sub { Mojo::File::path($ENV{CACHE_DIRECTORY} || $conf->{cache} || $home->child('cache')) });
$app->helper('paths.log' => sub { Mojo::File::path($ENV{LOGS_DIRECTORY} || $conf->{logs} || $home->child('log')) });
$app->helper('paths.run' => sub { Mojo::File::path($ENV{RUNTIME_DIRECTORY} || $conf->{runtime} || $home->child('run')) });
$app->helper('paths.state' => sub { Mojo::File::path($ENV{STATE_DIRECTORY} || $conf->{state} || $home) });
return if $conf->{no_chdir} // 1;
delete $conf->{chdir_to} unless $conf->{chdir_to} =~ /^(data|state)$/;
my $chdir = $conf->{chdir_to} ||= 'data';
return if $app->home->to_string eq $app->paths->$chdir->to_string;
$app->home(Mojo::Home->new($app->paths->$chdir));
warn sprintf "[app_paths] chdir %s (%sdir to backup: %s)\n", $app->home->to_string, $chdir, $app->home->realpath if chdir $app->home->to_string;
}
1;
package Mojolicious::Plugin::Listen;
use Mojo::Base 'Mojolicious::Plugin', -signatures;
use Mojo::Util qw(getopt);
sub register ($self, $app, $conf) {
$app->hook(before_command => sub ($command, $args) {
$command = ((split /::/, ref $command)[-1]);
return unless $command =~ /^(daemon|prefork|cgi|fcgi|morbo|psgi|standalone)$/;
$app->hook(before_server_start => sub ($server, $app) {
return if $ENV{MOJO_LISTEN};
getopt \@ARGV, ['pass_through'],
'l|listen=s' => \my $listen;
return if $listen;
$listen = $app->config->{listen};
$server->listen($listen) if $listen;
});
});
}
1;
package Mojolicious::Command::start;
use Mojo::Base 'Mojolicious::Command', -signatures;
use Mojo::Util qw(extract_usage getopt);
has description => 'Show versions of available modules';
has usage => sub { shift->extract_usage };
has perlbrew => sub {
return () unless $ENV{PERLBREW_ROOT} && $ENV{PERLBREW_HOME} && $ENV{PERLBREW_PERL};
return ['perlbrew', 'exec', '--with', join '@', grep {$_} $ENV{PERLBREW_PERL}, $ENV{PERLBREW_LIB}];
};
sub run ($self, @args) {
$ENV{MOJO_LISTEN} = join ',', $self->app->config->{listen}->@* if $self->app->config->{listen};
$ENV{MOJO_REVERSE_PROXY} = join ',', $self->app->config->{reverse_proxy} if $self->app->config->{reverse_proxy};
$ENV{MOJO_LOG_LEVEL} = join ',', $self->app->config->{log_level} if $self->app->config->{log_level};
$ENV{MOJO_LOG_SHORT} = join ',', $self->app->config->{log_short} if $self->app->config->{log_short};
$ENV{MOJO_LOG_PATH} = join ',', $self->app->config->{log_path} if $self->app->config->{log_path};
$ENV{MOJO_LOG_STDERR} = join ',', $self->app->config->{log_stderr} if $self->app->config->{log_stderr};
_exec(@{$self->perlbrew}, $self->app->config->{start}, @args) if $self->app->config->{start};
die "missing configuration key 'start'\n";
}
sub _exec {
$_ = join ' ', @_;
s/\$0/$0/g;
warn "$_\n";
exec $_;
}
1;
package Mojolicious::Plugin::Start;
use Mojo::Base 'Mojolicious::Plugin', -signatures;
use Mojo::File qw();
use Mojo::Util qw(trim);
use Time::HiRes qw(time);
has level => undef;
has path => undef;
has stderr => 0;
has start_time => time;
has user => sub { sub ($c) {} };
sub register ($self, $app, $conf) {
my $home = $app->home;
$app->helper('paths.app' => sub { $home });
$app->helper('paths.data' => sub { Mojo::File::path($ENV{MOJO_DATA} || $conf->{data} || $home) });
$app->helper('paths.tmp' => sub { Mojo::File::path($ENV{TMPDIR} || $conf->{tmp} || '/tmp') });
$app->helper('paths.vartmp' => sub { Mojo::File::path($conf->{vartmp} || '/var/tmp') });
$app->helper('paths.cache' => sub { Mojo::File::path($ENV{CACHE_DIRECTORY} || $conf->{cache} || $home->child('cache')) });
$app->helper('paths.log' => sub { Mojo::File::path($ENV{LOGS_DIRECTORY} || $conf->{logs} || $home->child('log')) });
$app->helper('paths.run' => sub { Mojo::File::path($ENV{RUNTIME_DIRECTORY} || $conf->{runtime} || $home->child('run')) });
$app->helper('paths.state' => sub { Mojo::File::path($ENV{STATE_DIRECTORY} || $conf->{state} || $home) });
$self->stderr(_is_true($ENV{MOJO_LOG_STDERR} || $conf->{log_stderr} || $app->config->{log_stderr}));
if (-d $app->paths->log) {
$self->path(Mojo::File::path($ENV{LOGS_DIRECTORY})->child($app->mode . '.log'));
}
elsif (my $log_path = $conf->{log_path} || $app->config->{log_path}) {
my $path = Mojo::File::path($log_path)->dirname if $log_path;
$self->path($path) if -d $path;
}
$app->log->path($self->path->realpath) if $self->path;
warn sprintf "[log_path] %s\n", Mojo::File::path($app->log->path) if $app->log->path && $self->stderr;
$self->level($ENV{MOJO_LOG_LEVEL} || $conf->{log_level} || $app->config->{log_level});
$app->log->level($self->level) if $self->level;
$app->log->on(message => sub ($log, $level, @lines) {
my $log_time = _is_true($ENV{MOJO_LOG_TIME} || $conf->{log_time} || $app->config->{log_time});
if ($self->stderr && ($log->path || defined $log_time)) {
warn _is_true($log_time) ? $log->format->(time, $level, @lines) : sprintf("[%.3f] [%s] %s\n", time - $self->start_time, $level, join " ", @lines);
}
});
$app->hook(before_render => sub ($c, $args) {
my $user = ref $conf->{user} eq 'CODE' ? $conf->{user}->($c) : '-';
return unless my $template = $args->{template};
if ($template eq 'exception') { # Make sure we are rendering the exception template
return unless my $e = $c->stash->{exception};
return unless my $snapshot = $c->stash->{snapshot};
my $cal = sprintf '%s#L%s', join('#', grep {$_} $snapshot->{controller}, $snapshot->{action}) || 'callback', $e->line->[0] || 0;
my @raptor = ($c->req->request_id, $args->{status}, $user, $c->req->url, $cal, $e);
$c->log->error(sprintf '[%s] [%s] [%s] Raptor Exception shown for %s: %s', @raptor[1..2], $cal, $c->req->url, trim $e);
$app->log->emit('raptor_exception', @raptor);
}
elsif ($template eq 'not_found') { # Make sure we are rendering the not_found template
my @raptor = ($c->req->request_id, $args->{status}, $user, $c->req->url);
$c->log->warn(sprintf '[%s] [%s] Raptor Not Found shown for %s', @raptor[1..$#raptor]);
$app->log->emit('raptor_not_found', @raptor);
}
});
return $self;
}
sub _is_false ($value) { return undef unless defined $value; $value =~ /^\s*(0|n|no|off|false)\s*$/i }
sub _is_true ($value) { return undef unless defined $value; $value =~ /^\s*(1|y|yes|on|true)\s*$/i }
1;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment