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.
- data/lib/apache_log_tail.rb +137 -0
- 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
|
+
|