#!/usr/bin/env perl
-
-# Usage:
-# gnunet-service |& gnunet-logread
-# gnunet-logread service.log
-#
-# Options:
-# -n <component_name> Name of this component to use for IPC logging.
-# -i </path/to/ipc.sock> Path to IPC logging socket.
-# Passing on log messages to IPC socket:
-# -L <LOGLEVEL> Minimum level of messages to pass on.
-# Log levels: NONE, ERROR, WARNING, INFO, DEBUG.
-# -m <regex> Only pass on messages matching a regular expression.
+# helper tool to make gnunet logs more readable
+# try 'gnunet-logread -h' for usage
use strict;
use warnings;
+my $DEFAULT_SOCKET = '/tmp/gnunet-logread-ipc.sock';
+
+print STDERR <<X if -t STDIN and $#ARGV == -1;
+*** For a usage message, try '$0 -h'.
+*** For documentation, try 'perldoc $0'.
+*** Listening for GNUNET_log events on STDIN. Type CTRL-D to terminate.
+
+X
use Getopt::Std;
+my (%opts, $name, $ipc, $msg_level, $msg_regex);
+getopts ('i:x:n:s:L:m:fhq', \%opts);
+
+use Pod::Usage qw( pod2usage );
+die pod2usage if $opts{h};
+
+use POSIX qw(mkfifo);
+
use Term::ANSIColor qw(:constants :pushpop);
$Term::ANSIColor::AUTOLOCAL = 1;
-my (%opts, $name, $ipc, $msg_level, $msg_regex);
-getopts ('n:i:L:m:', \%opts);
+my %levels = ( NONE => 0, ERROR => 1, WARNING => 2, INFO => 4, DEBUG => 8 );
# Message type numbers to names
my %msgtypes;
my $prefix = $ENV{GNUNET_PREFIX} || '/usr';
my $filename = "$prefix/include/gnunet/gnunet_protocols.h";
+$ipc = $opts{s} || $DEFAULT_SOCKET;
if (open HEADER, $filename)
{
$msgtypes{$2} = $1 if /^\s*#define\s+GNUNET_MESSAGE_TYPE_(\w+)\s+(\d+)/i;
}
close HEADER;
+} else {
+ warn <<X;
+Could not read $filename for message codes:
+ $!.
+Please provide a \$GNUNET_PREFIX environment variable to replace "/usr".
+Try also '$0 -h' for help.
+
+X
}
-else
-{
- warn "$filename: $!, try setting \$GNUNET_PREFIX";
+
+die "You can't read and write the socket at the same time"
+ if exists $opts{f} and exists $opts{n};
+
+if ((exists $opts{n} or exists $opts{f}) and not -r $ipc) {
+ undef $!;
+ die "Could not mkfifo $ipc: $!" unless mkfifo $ipc, 0600;
+ system('chgrp', 'gnunet', $ipc);
+ die "Could not chgrp $ipc to 'gnunet': $!" if $!;
+ chmod 0660, $ipc;
+ die "Could not chmod $ipc to allow gnunet group writes: $!" if $!;
}
-my %levels = ( NONE => 0, ERROR => 1, WARNING => 2, INFO => 4, DEBUG => 8 );
-if (exists $opts{n})
-{
+if (exists $opts{n}) {
$name = $opts{n};
- $ipc = $opts{i} || '/tmp/gnunet-logread-ipc.sock';
- $msg_level = exists $levels{$opts{L}} ? $levels{$opts{L}} : 0;
+ $msg_level = $opts{L} && exists $levels{$opts{L}} ? $levels{$opts{L}} : 0;
$msg_regex = $opts{m};
print STDERR "RE: /$msg_regex/\n" if defined $msg_regex;
- open IPC, '>', $ipc or die "$ipc: $!\n";
+ open O, '>', $ipc or die "Cannot write to $ipc: $!";
}
-while (<>)
-{
- if (fileno IPC) {
+if (exists $opts{f}) {
+ open(I, $ipc) or die "Cannot read from $ipc: $!";
+ &perform while <I>;
+ close I;
+} else {
+ &perform while <>;
+}
+fileno O and close O;
+exit;
+
+
+sub perform {
+ if (fileno O) {
my ($time, $type, $size, $from, $to, $level, $msg);
if (($time, $type, $size, $from, $to) =
/^([A-Z][a-z]{2}\ .[0-9]\ [0-9:]{8}(?:-[0-9]{6})?)\ util-.*\b
my ($time, $type, $size, $from, $to) = ($1, $2, $3,
$4 || $name, $5 || $name);
my $msg = exists $msgtypes{$type} ? $msgtypes{$type} : $type;
- my $ofh = select IPC;
- print IPC "$time\t$from -> $to\t$msg ($size)\n";
+ my $ofh = select O;
+ print O "$time\t$from -> $to\t$msg ($size)\n";
$| = 1;
select $ofh;
}
&& $levels{$level} <= $msg_level
&& (!defined $msg_regex || $msg =~ /$msg_regex/i)))
{
- print IPC "$time\t$name\t$level: $msg\n";
+ print O "$time\t$name\t$level: $msg\n";
}
}
+ return if $opts{x} and /$opts{x}/io;
+ return if $opts{i} and not /$opts{i}/io;
# Timestamp (e.g. Nov 01 19:36:11-384136)
s/^([A-Z][a-z]{2} .[0-9] [0-9:]{8}(?:-[0-9]{6})?)/YELLOW $1/e;
print;
}
-fileno IPC and close IPC;
+__END__
+
+=pod
+
+=head1 NAME
+
+gnunet-logread - a GNUnet log analyzer, colorizer and aggregator
+
+=head1 SYNOPSIS
+
+ <gnunet-service> |& $0 [<options>]
+ or
+ $0 [<options>] [<logfile>]
+
+ Options:
+ -f Follow input from IPC FIFO socket.
+
+ Regular screen output options:
+ -i <regex> Include only messages that match <regex>.
+ -x <regex> Exclude all messages that match <regex>.
+ -q Quiet: Do not show usage advice to new users.
+
+ Options to forward messages to the IPC FIFO socket:
+ -n <component_name> Name of the component we are forwarding messages for.
+ -s </path/to/ipc.sock> Default = $DEFAULT_SOCKET
+ -L <LOGLEVEL> Minimum level of messages to forward:
+ Log levels: NONE, ERROR, WARNING, INFO, DEBUG.
+ -m <regex> Only forward messages matching a regular expression.
+
+ See 'perldoc gnunet-logread' for a longer explanation.
+
+=head1 MOTIVATION
+
+GNUnet debug logs are a tedious read, but given a complex system that we
+cannot run all parts of in a debugger all the time, some gathering and
+structuring of events and message passing is useful.
+
+At first, this tool simply makes logs easier to read. Both if viewed in
+real-time or taken from disk. Then it also allows to extract all message
+passing events from it and forward them to a special process that aggregates
+all message passing events and therefore helps you make sense of all the
+inter-process communication (IPC) happening between the various pieces of
+the GNUnet system beast.
+
+That master process is simply an extra gnunet-logread that you run in a
+separate window and adorn it with the '-f' flag. The submitting processes
+instead need to be given a '-n' flag. That is because from the GNUnet logs
+it isn't clear which process events belong to. For example you may be
+having events taking place in the 'util' subsystem of gnunet-psyc-service
+just as much as in the 'util' subsystem of gnunet-multicast-service. In
+order to make sense of them it is necessary to manually add that info. This
+could be remedied by extending the semantics of the GNUNET_log facility
+instead, but that is still subject to further consideration.
+
+=head1 AUTHORS
+
+tg & lynX
+