1 # File: Stem/Log/Tail.pm
3 # This file is part of Stem.
4 # Copyright (C) 1999, 2000, 2001 Stem Systems, Inc.
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.
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.
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
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:
24 # Stem Systems, Inc. 781-643-7504
25 # 79 Everett St. info@stemsystems.com
29 package Stem::Log::Tail ;
35 use Stem::Trace 'log' => 'stem_status', 'sub' => 'TraceStatus' ;
36 use Stem::Trace 'log' => 'stem_error' , 'sub' => 'TraceError' ;
44 This is the full path to the file we want to tail.
52 This is the log which gets sent the data log entries.
57 'name' => 'status_log',
59 This is the log which gets sent the status log entries.
60 These include things like: Log has been rotated, deleted, moved, etc...
67 Label to tag tailed log entry.
74 Severity level for this tailed log entry.
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.
89 This specifies (in seconds) how long the delay is before the
90 first repeated checking of the log file for new data.
98 my( $class ) = shift ;
100 my $self = Stem::Class::parse_args( $attr_spec, @_ ) ;
101 return $self unless ref $self ;
103 #print "TAIL INT $self->{'interval'}\n" ;
105 if ( my $interval = $self->{'interval'} ) {
107 $self->{'timer'} = Stem::Event::Timer->new(
109 'method' => 'tail_cmd',
110 'interval' => $interval,
111 'delay' => $self->{'delay'},
116 #print "TIMER $self->{'timer'}\n" ;
120 $self->{'prev_size'} = 0 ;
121 $self->{'prev_mtime'} = 0 ;
122 $self->{'prev_inode'} = -1 ;
135 my $path = $self->{'path'} ;
137 unless( open( LOG, $path ) ) {
139 return if $self->{'open_failed'} ;
140 $self->{'open_failed'} = 1 ;
142 if ( my $status_log = $self->{'status_log'} ) {
144 Stem::Log::Entry->new(
145 'logs' => $status_log,
146 'label' => 'LogTail status',
148 "LogTail: missing log $path $!\n",
154 $self->{'open_failed'} = 0 ;
156 my( $inode, $size, $mtime ) = (stat LOG)[1, 7, 9] ;
158 TraceStatus "size $size mtime $mtime $inode" ;
160 my $prev_inode = $self->{'prev_inode'} ;
161 my $prev_size = $self->{'prev_size'} ;
163 if ( $prev_inode == -1 ) {
165 $self->{'prev_inode'} = $inode ;
167 if ( my $status_log = $self->{'status_log'} ) {
169 Stem::Log::Entry->new(
170 'logs' => $status_log,
172 'label' => 'LogTail status',
174 "LogTail: first open of $path\n",
178 elsif ( $inode != $prev_inode ) {
180 $self->{'prev_inode'} = $inode ;
182 if ( my $status_log = $self->{'status_log'} ) {
184 Stem::Log::Entry->new(
185 'logs' => $status_log,
187 'label' => 'LogTail status',
189 "LogTail: $path has moved\n",
193 # tail the entire file as it is new
198 elsif ( $size < $prev_size ) {
200 if ( my $status_log = $self->{'status_log'} ) {
202 Stem::Log::Entry->new(
203 'logs' => $status_log,
205 'label' => 'LogTail status',
207 "LogTail: $path has shrunk\n",
211 # tail the entire file as it has shrunk
215 elsif ( $size == $prev_size ) {
217 TraceStatus "no changes" ;
221 $self->{'prev_size'} = $size ;
223 my $delta_size = $size - $prev_size ;
225 return unless $delta_size ;
229 sysseek( *LOG, $prev_size, SEEK_SET ) ;
230 sysread( *LOG, $read_buf, $delta_size ) ;
232 Stem::Log::Entry->new(
233 'logs' => $self->{'data_log'},
234 'level' => $self->{'level'},
235 'label' => $self->{'label'},