apache_log_tail 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. data/lib/apache_log_tail.rb +137 -0
  2. metadata +65 -0
@@ -0,0 +1,137 @@
1
+
2
+ # Facilitates reading the most recent additions to a log file.
3
+ #
4
+ # Note that rotation of log files is *not* handled.
5
+ #
6
+ # Example:
7
+ # >> tail = LogTail.new "/var/log/apache2/access.log"
8
+ # >> tail.state_store.path_to_file = "/tmp/my-state.txt" # Optional: there is a default path
9
+ # >> tail.each_new_line {|line| puts line }
10
+ #
11
+ #
12
+ class LogTail
13
+
14
+ def initialize path_to_file
15
+ @path_to_file = path_to_file
16
+ end
17
+
18
+
19
+ # Provides a StateStore object that provides persistent storage of a Hash.
20
+ #
21
+ def state_store
22
+ @state_store ||= FileStateStore.new
23
+ end
24
+
25
+ # Accepts a StateStore object that provides persistent storage of a Hash.
26
+ #
27
+ # Example:
28
+ # >> tail.state_store = MyStateStore.new
29
+ #
30
+ # A StateStore object must provide these methods:
31
+ #
32
+ # - remember( state:Hash)
33
+ # - recall(): Hash
34
+ #
35
+ attr_writer :state_store
36
+
37
+
38
+ def each_new_line path_to_file = @path_to_file
39
+
40
+ # Recall the cursor ( the location in the log file where we left off
41
+ # reading last time)
42
+ state = state_store.recall
43
+ state[:cursor] ||= 0
44
+
45
+ File.open path_to_file do |stream|
46
+ # Move the file reading "head" to the place where we left off reading
47
+ # last time
48
+ stream.seek state[:cursor]
49
+
50
+ stream.each_line {|line| yield line }
51
+
52
+ # Remember where the log file reading cursor is for next time:
53
+ state[:cursor] = stream.tell
54
+ state_store.remember state
55
+ end
56
+ end
57
+
58
+
59
+ # This is the default implementation of StateStore, which stores the state in
60
+ # a file ( by default in /tmp with a static name although this is
61
+ # configurable).
62
+ #
63
+ class FileStateStore
64
+
65
+ def path_to_file
66
+ @path_to_file ||= "/tmp/.apache_log_tail-state.yml"
67
+ end
68
+ attr_writer :path_to_file
69
+
70
+ require "yaml"
71
+
72
+ def recall
73
+ if not File.exists? path_to_file
74
+ {}
75
+ else
76
+ YAML.load File.read( path_to_file)
77
+ end
78
+ end
79
+
80
+ def remember state
81
+ File.open path_to_file, "w" do |file|
82
+ file.write state.to_yaml
83
+ end
84
+ end
85
+
86
+ end # of FileStateStore
87
+
88
+ end
89
+
90
+
91
+ # Note that this class does no parse Apache log entries, only knows how Apache
92
+ # log files are rotated on Debian. I have enjoyed using the apachelogregex gem
93
+ # for parsing.
94
+ #
95
+ class ApacheLogTail < LogTail
96
+
97
+ # Note: This method must be invoked more frequently than the log file
98
+ # rotation period ( typically 1 week) otherwise an entire file will be
99
+ # missed.
100
+ #
101
+ def each_new_line
102
+ state = state_store.recall
103
+ first_line_now = first_line_of @path_to_file
104
+ # If the Apache log file has been rotated..
105
+ # The file has been rotated if it has been read ( cursor is remembered) but
106
+ # the first line is not as remembered
107
+ file_has_been_rotated = lambda { state[:cursor] and first_line_now != state[:first_line] }
108
+ if file_has_been_rotated[]
109
+ # Check that the renamed file is as we expect before reading the rest of it:
110
+ renamed_file = @path_to_file + ".1"
111
+ if first_line_of( renamed_file) != state[:first_line]
112
+ raise StandardError.new "Rotated file could not be found"
113
+ end
114
+ # Process the last lines of the rotated file:
115
+ super renamed_file
116
+ # Reset the cursor ready for the new file:
117
+ state[:cursor] = 0
118
+ end
119
+ if first_line_now != state[:first_line]
120
+ state[:first_line] = first_line_now
121
+ state_store.remember state
122
+ end
123
+ # Process the lines that have since been added to the new file:
124
+ super
125
+ end
126
+
127
+
128
+ private
129
+
130
+ def first_line_of file
131
+ File.open file do |f|
132
+ f.gets
133
+ end
134
+ end
135
+
136
+ end
137
+
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: apache_log_tail
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Neil Stockbridge
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-10-30 00:00:00 Z
19
+ dependencies: []
20
+
21
+ description: Presents lines from an Apache log file that were written since the previous invocation. Supports log file rotation.
22
+ email: neil@dist.ro
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files: []
28
+
29
+ files:
30
+ - lib/apache_log_tail.rb
31
+ homepage: http://rubygems.org/gems/apache_log_tail
32
+ licenses: []
33
+
34
+ post_install_message:
35
+ rdoc_options: []
36
+
37
+ require_paths:
38
+ - lib
39
+ required_ruby_version: !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 3
45
+ segments:
46
+ - 0
47
+ version: "0"
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ hash: 3
54
+ segments:
55
+ - 0
56
+ version: "0"
57
+ requirements: []
58
+
59
+ rubyforge_project:
60
+ rubygems_version: 1.8.15
61
+ signing_key:
62
+ specification_version: 3
63
+ summary: Process only the new lines from an Apache log file
64
+ test_files: []
65
+