eventmachine-tail 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/lib/em/filetail.rb +162 -0
  2. data/lib/em/globwatcher.rb +87 -0
  3. metadata +66 -0
@@ -0,0 +1,162 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "rubygems" if __FILE__ == $0
4
+ require "eventmachine"
5
+ require "logger"
6
+
7
+ EventMachine.epoll if EventMachine.epoll?
8
+
9
+ class EventMachine::FileTail
10
+ CHUNKSIZE = 65536
11
+ MAXSLEEP = 2
12
+
13
+ attr_reader :path
14
+
15
+ public
16
+ def initialize(path, startpos=0)
17
+ @path = path
18
+ @logger = Logger.new(STDOUT)
19
+
20
+ #@need_scheduling = true
21
+ open
22
+
23
+ @fstat = File.stat(@path)
24
+ @file.seek(0, IO::SEEK_END)
25
+ watch
26
+ end # def initialize
27
+
28
+ public
29
+ def notify(status)
30
+ @logger.debug("#{status} on #{path}")
31
+ if status == :modified
32
+ schedule_next_read
33
+ elsif status == :moved
34
+ # TODO(sissel): read to EOF, then reopen.
35
+ open
36
+ end
37
+ end
38
+
39
+ private
40
+ def open
41
+ @file.close if @file
42
+ begin
43
+ @file = File.open(@path, "r")
44
+ rescue Errno::ENOENT
45
+ # no file found
46
+ raise
47
+ end
48
+
49
+ @naptime = 0;
50
+ @pos = 0
51
+ schedule_next_read
52
+ end
53
+
54
+ private
55
+ def watch
56
+ EventMachine::watch_file(@path, FileWatcher, self)
57
+ end
58
+
59
+ private
60
+ def schedule_next_read
61
+ EventMachine::add_timer(@naptime) do
62
+ read
63
+ end
64
+ end
65
+
66
+ private
67
+ def read
68
+ begin
69
+ data = @file.sysread(CHUNKSIZE)
70
+ # Won't get here if sysread throws EOF
71
+ @pos += data.length
72
+ @naptime = 0
73
+ receive_data(data)
74
+ schedule_next_read
75
+ rescue EOFError
76
+ eof
77
+ end
78
+ end
79
+
80
+ private
81
+ def eof
82
+ # TODO(sissel): This will be necessary if we can't use inotify or kqueue to
83
+ # get notified of file changes
84
+ #if @need_scheduling
85
+ #@naptime = 0.100 if @naptime == 0
86
+ #@naptime *= 2
87
+ #@naptime = MAXSLEEP if @naptime > MAXSLEEP
88
+ #@logger.info("EOF. Naptime: #{@naptime}")
89
+ #end
90
+
91
+ # TODO(sissel): schedule an fstat instead of doing it now.
92
+ fstat = File.stat(@path)
93
+ handle_fstat(fstat)
94
+ end # def eof
95
+
96
+ def handle_fstat(fstat)
97
+ if (fstat.ino != @fstat.ino)
98
+ open # Reopen if the inode has changed
99
+ elsif (fstat.rdev != @fstat.rdev)
100
+ open # Reopen if the filesystem device changed
101
+ elsif (fstat.size < @fstat.size)
102
+ @logger.info("File likely truncated... #{path}")
103
+ @file.seek(0, IO::SEEK_SET)
104
+ schedule_next_read
105
+ end
106
+ @fstat = fstat
107
+ end # def eof
108
+ end # class EventMachine::FileTail
109
+
110
+ # Internal usage only
111
+ class EventMachine::FileTail::FileWatcher < EventMachine::FileWatch
112
+ def initialize(filestream)
113
+ @filestream = filestream
114
+ @logger = Logger.new(STDOUT)
115
+ end
116
+
117
+ def file_modified
118
+ @filestream.notify :modified
119
+ end
120
+
121
+ def file_moved
122
+ @filestream.notify :moved
123
+ end
124
+
125
+ def file_deleted
126
+ @filestream.notify :deleted
127
+ end
128
+
129
+ def unbind
130
+ @filestream.notify :unbind
131
+ end
132
+ end # class EventMachine::FileTail::FileWatch < EventMachine::FileWatch
133
+
134
+ # Add EventMachine::file_tail
135
+ module EventMachine
136
+ def self.file_tail(path, handler=nil, *args)
137
+ args.unshift(path)
138
+ klass = klass_from_handler(EventMachine::FileTail, handler, *args);
139
+ c = klass.new(*args)
140
+ yield c if block_given?
141
+ return c
142
+ end # def self.file_tail
143
+ end # module EventMachine
144
+
145
+ if __FILE__ == $0
146
+ class Reader < EventMachine::FileTail
147
+ def initialize(*args)
148
+ super(*args)
149
+ @buffer = BufferedTokenizer.new
150
+ end
151
+
152
+ def receive_data(data)
153
+ @buffer.extract(data).each do |line|
154
+ ap [path, line]
155
+ end
156
+ end
157
+ end
158
+
159
+ EventMachine::run do
160
+ fw = EventMachine::filestream("/var/log/user.log", Reader)
161
+ end
162
+ end # if __FILE__ == $0
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "rubygems" if __FILE__ == $0
4
+ require "set"
5
+ require "eventmachine"
6
+ require "ap"
7
+ require "logger"
8
+
9
+ require "filetail"
10
+
11
+ class EventMachine::FileGlobWatch
12
+ def initialize(pathglob, handler, interval=60)
13
+ @pathglob = pathglob
14
+ @handler = handler
15
+ @files = Set.new
16
+
17
+ EM.next_tick do
18
+ find_files
19
+ EM.add_periodic_timer(interval) do
20
+ find_files
21
+ end
22
+ end
23
+ end # def initialize
24
+
25
+ def find_files
26
+ list = Set.new(Dir.glob(@pathglob))
27
+ list.each do |path|
28
+ next if @files.include?(path)
29
+ watch(path)
30
+ end
31
+
32
+ (@files - list).each do |missing|
33
+ @files.delete(missing)
34
+ @handler.file_removed(missing)
35
+ end
36
+ end # def find_files
37
+
38
+ def watch(path)
39
+ puts "Watching #{path}"
40
+ @files.add(path)
41
+ @handler.file_found(path)
42
+ end # def watch
43
+ end # class EventMachine::FileGlobWatch
44
+
45
+ class EventMachine::FileGlobWatchHandler
46
+ LOGGER = Logger.new(STDOUT)
47
+ def initialize(handler=nil)
48
+ @handler = handler
49
+ end
50
+
51
+ def file_found(path)
52
+ EventMachine::file_tail(path, @handler)
53
+ end
54
+
55
+ def file_removed(path)
56
+ # Nothing to do
57
+ end
58
+ end # class EventMachine::FileGlobWatchHandler
59
+
60
+ module EventMachine
61
+ def self.glob_tail(glob, handler=nil, *args)
62
+ handler = EventMachine::FileGlobHandler if handler == nil
63
+ klass = klass_from_handler(EventMachine::FileGlobWatchHandler, handler, *args)
64
+ c = klass.new(*args)
65
+ yield c if block_given?
66
+ return c
67
+ end
68
+ end
69
+
70
+ if __FILE__ == $0
71
+ class Reader < EventMachine::FileTail
72
+ def initialize(*args)
73
+ super(*args)
74
+ @buffer = BufferedTokenizer.new
75
+ end
76
+
77
+ def receive_data(data)
78
+ @buffer.extract(data).each do |line|
79
+ ap [path, line]
80
+ end
81
+ end
82
+ end
83
+
84
+ EventMachine.run do
85
+ EventMachine::FileGlobWatch.new("/var/log/*.log", EventMachine::FileGlobWatchHandler.new(Reader))
86
+ end
87
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: eventmachine-tail
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Jordan Sissel
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-04-11 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: eventmachine
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ description: Add file 'tail' implemented with EventMachine
26
+ email: jls@semicomplete.com
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files: []
32
+
33
+ files:
34
+ - lib/em/filetail.rb
35
+ - lib/em/globwatcher.rb
36
+ has_rdoc: true
37
+ homepage: http://code.google.com/p/semicomplete/wiki/EventMachine-Tail
38
+ licenses: []
39
+
40
+ post_install_message:
41
+ rdoc_options: []
42
+
43
+ require_paths:
44
+ - lib
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: "0"
51
+ version:
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ version:
58
+ requirements: []
59
+
60
+ rubyforge_project:
61
+ rubygems_version: 1.3.5
62
+ signing_key:
63
+ specification_version: 3
64
+ summary: eventmachine tail - a file tail implementation
65
+ test_files: []
66
+