Improved: select the number of lines to show.
Added Manpage and Help.
--- a/hlog Tue Jan 27 21:28:55 2009 +0100
+++ b/hlog Tue Jan 27 22:30:02 2009 +0100
@@ -24,9 +24,10 @@
use IO::Socket::INET;
use Pod::Usage;
-my $opt_addr = "0.0.0.0";
-my $opt_port = 8080;
-my $logfile = "hlog.log";
+my $opt_addr = "0.0.0.0";
+my $opt_port = 8080;
+my $opt_lines = 10;
+my $logfile = "hlog.log";
my $FILE;
sub handle_request($);
@@ -37,10 +38,14 @@
MAIN: {
GetOptions(
- "addr=s" => \$opt_addr,
- "port=i" => \$opt_port,
+ "addr=s" => \$opt_addr,
+ "port=i" => \$opt_port,
+ "lines=i" => \$opt_lines,
+ "help" => sub { pod2usage(-verbose => 1, -exitval => 0) },
+ "man" => sub { pod2usage(-verbose => 2, -exitval => 0) },
) or pod2usage();
+
open(LOG, ">>$logfile");
print LOG localtime() . " started\n";
@@ -57,12 +62,13 @@
warn "listener $opt_addr:$opt_port\n";
while (my $client = $listener->accept) {
- print LOG $_ = localtime()
+ print LOG $_ =
+ localtime()
. " access from "
. $client->peerhost . ":"
. $client->peerport . "\n";
- warn $_;
+ warn $_;
my $pid = fork();
die "Can't fork: $!\n" if not defined $pid;
@@ -81,7 +87,7 @@
local $_ = <$client>;
# should be HTTP/x.x
- if (not s/HTTP\/\S+\s*$//) {
+ if (not s/\s+HTTP\/\S+\s*$//) {
$client->print(bad_request);
return;
}
@@ -91,18 +97,66 @@
$client->print(http "400 Bad Request" => bad_request);
}
- open(my $file, $FILE);
+ my $lines = /(\d+)$/ ? $1 : $opt_lines;
+
+ # read the header(s) and discard
+ while (<$client>) { last if /^\s*$/ }
+
+ # number of lines to show
- $client->print(http "200 OK" => join "", <<__EOF, <$file>);
+ my %file = analyze($FILE);
+ if (!%file) {
+ $client->print(http "500 internal error");
+ print LOG $@;
+ return;
+ }
+
+ seek($file{fh}, -($lines + 1) * $file{avglen}, 2);
+ $file{fh}->getline;
+
+ $client->print(http "200 OK" => join "", <<__EOF, $file{fh}->getlines);
# Proof of concept ;-)
# see https://keller.schlittermann.de/hg/hlog
#
-# FILE: $FILE
+# FILE: @{[sprintf "%s", $file{name}]}
+# LENGTH: @{[sprintf "%5d", $file{size}]}
+# LINES: @{[sprintf "%5d approx", $file{lines}]}
+# LENGTH: @{[sprintf "%5d approx", $file{avglen}]}
+# DISPLAY: @{[sprintf "%5d approx", $lines]}
+#
+# append /<number> to your request to select the number of displayed
+# lines
#
__EOF
}
+sub analyze($) {
+ my %r;
+ $r{name} = shift;
+ $r{size} = -s $r{name};
+ open($r{fh}, $r{name}) or do {
+ $@ = "Can't open $r{name}: $!\n";
+ return ();
+ };
+
+ if ($r{size} == 0) {
+ $r{lines} = 0;
+ }
+ else {
+ my $s;
+ while (defined($_ = $r{fh}->getline)) {
+ $s += length;
+ last if $. == 100;
+ }
+ $r{avglen} = $s / $.;
+ $r{lines} = int($r{size} / $r{avglen});
+ }
+
+ seek($r{fh}, 0, 0);
+ return %r;
+}
+
sub http($@) {
my $code = shift;
my $date = date1123();
@@ -149,11 +203,30 @@
=head1 SYNOPSIS
- hlog [-p|--port port] [-a|--address address] file
+ hlog [--lines n] [-p|--port port] [-a|--address address] file
+ hlog [-h|--help] [-m|--man]
=head1 DESCRIPTION
This script should run as a server providing access to
the last lines of a logfile. It should understand basic HTTP/1.x.
+=head1 OPTIONS
+
+=over
+
+=item B<-p>|B<--port> I<port>
+
+The port to listen on. (default: 8080)
+
+=item B<-a>|B<--address> I<address>
+
+The address to listen on. (default: 0.0.0.0)
+
+=item B<--lines> I<lines>
+
+The number of lines to show. (default: 10)
+
+=back
+
=cut