Skip to content

Instantly share code, notes, and snippets.

@zakame
Last active March 6, 2021 22:20
Show Gist options
  • Save zakame/34927be84d860a45d7add5b35270761f to your computer and use it in GitHub Desktop.
Save zakame/34927be84d860a45d7add5b35270761f to your computer and use it in GitHub Desktop.
Tail a logfile with Mojolicious (for example /var/log/messages)
#!/usr/bin/env perl
use Mojolicious::Lite;
package Model::FileWatch {
use Mojo::Base 'Mojo::EventEmitter';
use Mojo::Promise;
use IO::File;
has 'file';
sub watch {
my $self = shift;
my $p = Mojo::Promise->new;
my $fh = IO::File->new;
unless ($fh->open($self->file, 'r')) {
return $p->reject("watching @{[$self->file]} failed: $!");
}
$fh->seek(0, 2);
my $id = $p->ioloop->recurring(
0.25 => sub {
$fh->seek(0, 1);
if (my $line = $fh->getline) {
$self->emit(oneline => $line);
}
}
);
$self->once(finish => sub { $p->ioloop->remove($id) });
$p;
}
}
websocket '/tail' => sub {
my $c = shift;
my $m = Model::FileWatch->new(file => '/var/log/messages');
$c->inactivity_timeout(300);
$m->on(
oneline => sub {
my ($tail, $line) = @_;
$c->send($line);
}
);
$c->on(finish => sub { $m->emit('finish')->unsubscribe('oneline') });
$m->watch->catch(sub { $c->send(@_) })->wait;
};
get '/' => 'index';
app->start;
__DATA__
@@ index.html.ep
<!DOCTYPE html>
<html>
<head><title>Tail</title></head>
<body>
<script>
var ws = new WebSocket('<%= url_for('tail')->to_abs %>');
// Incoming messages
ws.onmessage = function (event) {
document.body.innerHTML += event.data + '<br/>';
window.scrollTo(0, document.body.scrollHeight);
};
// Ping from client side to keep WS connection alive
window.setInterval(function () { ws.send('') }, 10000);
</script>
</body>
</html>
__END__
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment