Commit | Line | Data |
4536f655 |
1 | # File: Stem/Log/Tail.pm |
2 | |
3 | # This file is part of Stem. |
4 | # Copyright (C) 1999, 2000, 2001 Stem Systems, Inc. |
5 | |
6 | # Stem is free software; you can redistribute it and/or modify |
7 | # it under the terms of the GNU General Public License as published by |
8 | # the Free Software Foundation; either version 2 of the License, or |
9 | # (at your option) any later version. |
10 | |
11 | # Stem is distributed in the hope that it will be useful, |
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | # GNU General Public License for more details. |
15 | |
16 | # You should have received a copy of the GNU General Public License |
17 | # along with Stem; if not, write to the Free Software |
18 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
19 | |
20 | # For a license to use the Stem under conditions other than those |
21 | # described here, to purchase support for this software, or to purchase a |
22 | # commercial warranty contract, please contact Stem Systems at: |
23 | |
24 | # Stem Systems, Inc. 781-643-7504 |
25 | # 79 Everett St. info@stemsystems.com |
26 | # Arlington, MA 02474 |
27 | # USA |
28 | |
29 | package Stem::Log::Tail ; |
30 | |
31 | use strict ; |
32 | use IO::Seekable ; |
33 | use Data::Dumper ; |
34 | |
35 | use Stem::Trace 'log' => 'stem_status', 'sub' => 'TraceStatus' ; |
36 | use Stem::Trace 'log' => 'stem_error' , 'sub' => 'TraceError' ; |
37 | |
38 | my $attr_spec = [ |
39 | |
40 | { |
41 | 'name' => 'path', |
42 | 'required' => 1, |
43 | 'help' => <<HELP, |
44 | This is the full path to the file we want to tail. |
45 | HELP |
46 | }, |
47 | |
48 | { |
49 | 'name' => 'data_log', |
50 | 'required' => 1, |
51 | 'help' => <<HELP, |
52 | This is the log which gets sent the data log entries. |
53 | HELP |
54 | }, |
55 | |
56 | { |
57 | 'name' => 'status_log', |
58 | 'help' => <<HELP, |
59 | This is the log which gets sent the status log entries. |
60 | These include things like: Log has been rotated, deleted, moved, etc... |
61 | HELP |
62 | }, |
63 | { |
64 | 'name' => 'label', |
65 | 'default' => 'tail', |
66 | 'help' => <<HELP, |
67 | Label to tag tailed log entry. |
68 | HELP |
69 | }, |
70 | { |
71 | 'name' => 'level', |
72 | 'default' => '5', |
73 | 'help' => <<HELP, |
74 | Severity level for this tailed log entry. |
75 | HELP |
76 | }, |
77 | { |
78 | 'name' => 'interval', |
79 | 'help' => <<HELP, |
80 | This specifies (in seconds) how often we check the log file for new |
81 | data. If this is not specified, you need to call the tail_cmd method |
82 | to check for new data. |
83 | HELP |
84 | }, |
85 | { |
86 | 'name' => 'delay', |
87 | 'default' => 10, |
88 | 'help' => <<HELP, |
89 | This specifies (in seconds) how long the delay is before the |
90 | first repeated checking of the log file for new data. |
91 | HELP |
92 | }, |
93 | ] ; |
94 | |
95 | |
96 | sub new { |
97 | |
98 | my( $class ) = shift ; |
99 | |
100 | my $self = Stem::Class::parse_args( $attr_spec, @_ ) ; |
101 | return $self unless ref $self ; |
102 | |
103 | print "TAIL INT $self->{'interval'}\n" ; |
104 | |
105 | if ( my $interval = $self->{'interval'} ) { |
106 | |
107 | $self->{'timer'} = Stem::Event::Timer->new( |
108 | 'object' => $self, |
109 | 'method' => 'tail_cmd', |
110 | 'interval' => $interval, |
111 | 'delay' => $self->{'delay'}, |
112 | 'repeat' => 1, |
113 | 'hard' => 1, |
114 | ) ; |
115 | |
116 | print "TIMER $self->{'timer'}\n" ; |
117 | |
118 | } |
119 | |
120 | $self->{'prev_size'} = 0 ; |
121 | $self->{'prev_mtime'} = 0 ; |
122 | $self->{'prev_inode'} = -1 ; |
123 | |
124 | return( $self ) ; |
125 | } |
126 | |
127 | sub tail_cmd { |
128 | |
129 | my( $self ) = @_ ; |
130 | |
131 | print "TAILING\n" ; |
132 | |
133 | local( *LOG ) ; |
134 | |
135 | my $path = $self->{'path'} ; |
136 | |
137 | unless( open( LOG, $path ) ) { |
138 | |
139 | return if $self->{'open_failed'} ; |
140 | $self->{'open_failed'} = 1 ; |
141 | |
142 | if ( my $status_log = $self->{'status_log'} ) { |
143 | |
144 | Stem::Log::Entry->new( |
145 | 'logs' => $status_log, |
146 | 'label' => 'LogTail status', |
147 | 'text' => |
148 | "LogTail: missing log $path $!\n", |
149 | ) ; |
150 | } |
151 | return ; |
152 | } |
153 | |
154 | $self->{'open_failed'} = 0 ; |
155 | |
156 | my( $inode, $size, $mtime ) = (stat LOG)[1, 7, 9] ; |
157 | |
158 | TraceStatus "size $size mtime $mtime $inode" ; |
159 | |
160 | my $prev_inode = $self->{'prev_inode'} ; |
161 | my $prev_size = $self->{'prev_size'} ; |
162 | |
163 | if ( $prev_inode == -1 ) { |
164 | |
165 | $self->{'prev_inode'} = $inode ; |
166 | |
167 | if ( my $status_log = $self->{'status_log'} ) { |
168 | |
169 | Stem::Log::Entry->new( |
170 | 'logs' => $status_log, |
171 | 'level' => 6, |
172 | 'label' => 'LogTail status', |
173 | 'text' => |
174 | "LogTail: first open of $path\n", |
175 | ) ; |
176 | } |
177 | } |
178 | elsif ( $inode != $prev_inode ) { |
179 | |
180 | $self->{'prev_inode'} = $inode ; |
181 | |
182 | if ( my $status_log = $self->{'status_log'} ) { |
183 | |
184 | Stem::Log::Entry->new( |
185 | 'logs' => $status_log, |
186 | 'level' => 6, |
187 | 'label' => 'LogTail status', |
188 | 'text' => |
189 | "LogTail: $path has moved\n", |
190 | ) ; |
191 | } |
192 | |
193 | # tail the entire file as it is new |
194 | |
195 | $prev_size = 0 ; |
196 | |
197 | } |
198 | elsif ( $size < $prev_size ) { |
199 | |
200 | if ( my $status_log = $self->{'status_log'} ) { |
201 | |
202 | Stem::Log::Entry->new( |
203 | 'logs' => $status_log, |
204 | 'level' => 6, |
205 | 'label' => 'LogTail status', |
206 | 'text' => |
207 | "LogTail: $path has shrunk\n", |
208 | ) ; |
209 | } |
210 | |
211 | # tail the entire file as it has shrunk |
212 | |
213 | $prev_size = 0 ; |
214 | } |
215 | elsif ( $size == $prev_size ) { |
216 | |
217 | TraceStatus "no changes" ; |
218 | return ; |
219 | } |
220 | |
221 | $self->{'prev_size'} = $size ; |
222 | |
223 | my $delta_size = $size - $prev_size ; |
224 | |
225 | return unless $delta_size ; |
226 | |
227 | my $read_buf ; |
228 | |
229 | sysseek( *LOG, $prev_size, SEEK_SET ) ; |
230 | sysread( *LOG, $read_buf, $delta_size ) ; |
231 | |
232 | Stem::Log::Entry->new( |
233 | 'logs' => $self->{'data_log'}, |
234 | 'level' => $self->{'level'}, |
235 | 'label' => $self->{'label'}, |
236 | 'text' => $read_buf, |
237 | ) ; |
238 | |
239 | return ; |
240 | } |
241 | |
242 | 1 ; |