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