b769ccdcc85f039d03517b0d18c4a83936667ca9
[usenet/newsstats.git] / bin / feedlog.pl
1 #! /usr/bin/perl
2 #
3 # feedlog.pl
4 #
5 # This script will log headers and other data to a database
6 # for further analysis by parsing a feed from INN.
7 #
8 # It is part of the NewsStats package.
9 #
10 # Copyright (c) 2010-2013 Thomas Hochstein <thh@inter.net>
11 #
12 # It can be redistributed and/or modified under the same terms under
13 # which Perl itself is published.
14
15 BEGIN {
16   our $VERSION = "0.02";
17   use File::Basename;
18   # we're in .../bin, so our module is in ../lib
19   push(@INC, dirname($0).'/../lib');
20 }
21 use strict;
22 use warnings;
23
24 use NewsStats;
25
26 use Sys::Syslog qw(:standard :macros);
27
28 use Date::Format;
29 use DBI;
30 use Getopt::Long qw(GetOptions);
31 Getopt::Long::config ('bundling');
32
33 ################################# Subroutines ##################################
34
35 sub PrepareDB {
36 ### initialise database connection, prepare statement
37 ### and catch errors
38 ### IN : \%Conf   : reference to configuration hash
39 ### OUT: $DBHandle: database handle
40 ###      $DBQuery : prepared statement
41   our ($DBHandle, $DBQuery, $OptQuiet);
42   my ($ConfigR) = @_;
43   my %Conf = %$ConfigR;
44   # drop current database connection - hard, if necessary
45   if ($DBHandle) {
46     $DBHandle->disconnect;
47     undef $DBHandle;
48   };
49   # connect to database; try again every 5 seconds
50   while (!$DBHandle) {
51     $DBHandle = InitDB($ConfigR,0);
52     if (!$DBHandle) {
53       syslog(LOG_CRIT, 'Database connection failed: %s', $DBI::errstr);
54       sleep(5);
55     } else {;
56       syslog(LOG_NOTICE, "Database connection (re-)established successfully.") if !$OptQuiet;
57     }
58   };
59   $DBQuery = $DBHandle->prepare(sprintf("INSERT INTO %s.%s (day,date,mid,
60                                          timestamp,token,size,peer,path,
61                                          newsgroups,headers)
62                                          VALUES (?,?,?,?,?,?,?,?,?,?)",
63                                          $Conf{'DBDatabase'},
64                                          $Conf{'DBTableRaw'}));
65   return ($DBHandle,$DBQuery);
66 }
67
68
69 ################################# Main program #################################
70
71 ### read commandline options
72 my ($OptDebug,$OptQuiet,$OptConfFile);
73 GetOptions ('d|debug!'        => \$OptDebug,
74             'q|test!'         => \$OptQuiet,
75             'conffile=s'      => \$OptConfFile,
76             'h|help'          => \&ShowPOD,
77             'V|version'       => \&ShowVersion) or exit 1;
78
79 ### read configuration
80 my %Conf = %{ReadConfig($OptConfFile)};
81
82 ### init syslog
83 openlog($0, 'nofatal,pid', LOG_NEWS);
84 syslog(LOG_NOTICE, "$MyVersion starting up.") if !$OptQuiet;
85
86 ### init database
87 my ($DBHandle,$DBQuery) = PrepareDB(\%Conf);
88
89 ### main loop
90 while (<>) {
91   chomp;
92   # catch empty lines trailing or leading
93   if ($_ eq '') {
94     next;
95   }
96   # first line contains: mid, timestamp, token, size, peer, Path, Newsgroups
97   my ($Mid, $Timestamp, $Token, $Size, $Peer, $Path, $Newsgroups) = split;
98   # remaining lines contain headers
99   my $Headers = "";
100   while (<>) {
101     chomp;
102     # empty line terminates this article
103     if ($_ eq '') {
104       last;
105     }
106     # collect headers
107     $Headers .= $_."\n" ;
108   }
109
110   # parse timestamp to day (YYYY-MM-DD) and to MySQL timestamp
111   my $Day  = time2str("%Y-%m-%d", $Timestamp);
112   my $Date = time2str("%Y-%m-%d %H:%M:%S", $Timestamp);
113
114   # write to database
115   if (!$DBQuery->execute($Day, $Date, $Mid, $Timestamp, $Token, $Size, $Peer,
116                          $Path, $Newsgroups, $Headers)) {
117     syslog(LOG_ERR, 'Database error %s while processing %s: %s',
118            $DBI::err, $Mid, $DBI::errstr);
119     # if "MySQL server has gone away", try to recover
120     if ($DBI::err == 2006) {
121       # try to reconnect to database
122       ($DBHandle,$DBQuery) = PrepareDB(\%Conf);
123       # try to repeat the write attempt as before
124       if (!$DBQuery->execute($Day, $Date, $Mid, $Timestamp, $Token, $Size, $Peer,
125                              $Path, $Newsgroups, $Headers)) {
126         syslog(LOG_ERR, '%s was dropped and lost.',$Mid);
127       };
128     # otherwise log missing posting
129     } else {
130       syslog(LOG_ERR, '%s was dropped and lost.',$Mid);
131     };
132   };
133   $DBQuery->finish;
134
135   warn sprintf("-----\nDay: %s\nDate: %s\nMID: %s\nTS: %s\nToken: %s\n".
136                "Size: %s\nPeer: %s\nPath: %s\nNewsgroups: %s\nHeaders: %s\n",
137                $Day, $Date, $Mid, $Timestamp, $Token, $Size, $Peer, $Path,
138                $Newsgroups, $Headers) if $OptDebug;
139 }
140
141 ### close handles
142 $DBHandle->disconnect;
143 syslog(LOG_NOTICE, "$0 closing down.") if !$OptQuiet;
144 closelog();
145
146 __END__
147
148 ################################ Documentation #################################
149
150 =head1 NAME
151
152 feedlog - log data from an INN feed to a database
153
154 =head1 SYNOPSIS
155
156 B<feedlog> [B<-Vhdq>] [--conffile I<filename>]
157
158 =head1 REQUIREMENTS
159
160 See L<doc/README>.
161
162 =head1 DESCRIPTION
163
164 This script will log overview data and complete headers to a database
165 table for further examination by parsing a feed from INN. It will
166 parse that information and write it to a mysql database table in real
167 time.
168
169 All reporting is done to I<syslog> via I<news> facility. If B<feedlog>
170 fails to initiate a database connection at startup, it will log to
171 I<syslog> with I<CRIT> priority and go in an endless loop, as
172 terminating would only result in a rapid respawn.
173
174 =head2 Configuration
175
176 B<feedlog> will read its configuration from F<newsstats.conf> which
177 should be present in the same directory via Config::Auto.
178
179 See L<doc/INSTALL> for an overview of possible configuration options.
180
181 =head1 OPTIONS
182
183 =over 3
184
185 =item B<-V>, B<--version>
186
187 Print out version and copyright information and exit.
188
189 =item B<-h>, B<--help>
190
191 Print this man page and exit.
192
193 =item B<-d>, B<--debug>
194
195 Output debugging information to STDERR while parsing STDIN. You'll
196 find that information most probably in your B<INN> F<errlog> file.
197
198 =item B<-q>, B<--quiet>
199
200 Suppress logging to syslog.
201
202 =item B<--conffile> I<filename>
203
204 Load configuration from I<filename> instead of F<newsstats.conf>.
205
206 =back
207
208 =head1 INSTALLATION
209
210 See L<doc/INSTALL>.
211
212 =head1 EXAMPLES
213
214 Set up a feed like that in your B<INN> F<newsfeeds> file:
215
216     ## gather statistics for NewsStats
217     newsstats!
218             :!*,de.*
219             :Tc,WmtfbsPNH,Ac:/path/to/feedlog.pl
220
221 See L<doc/INSTALL> for further information.
222
223 =head1 FILES
224
225 =over 4
226
227 =item F<bin/feedlog.pl>
228
229 The script itself.
230
231 =item F<lib/NewsStats.pm>
232
233 Library functions for the NewsStats package.
234
235 =item F<etc/newsstats.conf>
236
237 Runtime configuration file.
238
239 =back
240
241 =head1 BUGS
242
243 Please report any bugs or feature requests to the author or use the
244 bug tracker at L<http://bugs.th-h.de/>!
245
246 =head1 SEE ALSO
247
248 =over 2
249
250 =item -
251
252 L<doc/README>
253
254 =item -
255
256 L<doc/INSTALL>
257
258 =back
259
260 This script is part of the B<NewsStats> package.
261
262 =head1 AUTHOR
263
264 Thomas Hochstein <thh@inter.net>
265
266 =head1 COPYRIGHT AND LICENSE
267
268 Copyright (c) 2010-2013 Thomas Hochstein <thh@inter.net>
269
270 This program is free software; you may redistribute it and/or modify it
271 under the same terms as Perl itself.
272
273 =cut
This page took 0.016887 seconds and 3 git commands to generate.