snoopit 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.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +39 -0
- data/.idea/.name +1 -0
- data/.idea/.rakeTasks +7 -0
- data/.idea/dictionaries/rbirch.xml +9 -0
- data/.idea/encodings.xml +5 -0
- data/.idea/misc.xml +5 -0
- data/.idea/modules.xml +9 -0
- data/.idea/scopes/scope_settings.xml +5 -0
- data/.idea/snoopit.iml +233 -0
- data/.idea/vcs.xml +7 -0
- data/.rspec +2 -0
- data/.travis.yml +7 -0
- data/Gemfile +15 -0
- data/LICENSE.txt +22 -0
- data/README.md +411 -0
- data/Rakefile +1 -0
- data/bin/snoopit +173 -0
- data/lib/snoopit.rb +22 -0
- data/lib/snoopit/detected.rb +50 -0
- data/lib/snoopit/file_info.rb +104 -0
- data/lib/snoopit/file_tracker.rb +83 -0
- data/lib/snoopit/logger.rb +30 -0
- data/lib/snoopit/notification_manager.rb +123 -0
- data/lib/snoopit/notifier.rb +25 -0
- data/lib/snoopit/notifiers/email.rb +61 -0
- data/lib/snoopit/notifiers/http.rb +85 -0
- data/lib/snoopit/notifiers/https.rb +21 -0
- data/lib/snoopit/notifiers/stomp.rb +59 -0
- data/lib/snoopit/register.rb +69 -0
- data/lib/snoopit/sniffer.rb +51 -0
- data/lib/snoopit/snooper.rb +149 -0
- data/lib/snoopit/snoopy.rb +67 -0
- data/lib/snoopit/version.rb +3 -0
- data/snoopit.gemspec +27 -0
- data/spec/bin/snoopit_spec.rb +258 -0
- data/spec/file_info_spec.rb +131 -0
- data/spec/file_tracker_spec.rb +172 -0
- data/spec/notification_manager_spec.rb +103 -0
- data/spec/notifiers/email_spec.rb +36 -0
- data/spec/notifiers/http_spec.rb +37 -0
- data/spec/notifiers/https_spec.rb +38 -0
- data/spec/notifiers/stomp_spec.rb +34 -0
- data/spec/register_spec.rb +105 -0
- data/spec/snooper_spec.rb +538 -0
- data/spec/spec_helper.rb +24 -0
- data/spec/support/log/snoop_log.test +593 -0
- data/spec/support/log/snoop_log_2.test +593 -0
- data/spec/support/multiple_snoopies.json +82 -0
- data/spec/support/regexp_tester.rb +10 -0
- data/spec/support/snoopies.json +93 -0
- data/spec/support/snoopies_notifiers.json +66 -0
- data/spec/support/test_notifier.rb +18 -0
- data/spec/support/test_notifier_load.rb +18 -0
- data/support/snoopies.json +110 -0
- metadata +190 -0
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/snoopit
ADDED
@@ -0,0 +1,173 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'optparse'
|
3
|
+
require 'logger'
|
4
|
+
require 'snoopit'
|
5
|
+
|
6
|
+
options = {}
|
7
|
+
options[:snoopies_file]='snoopies.json'
|
8
|
+
options[:snoopies] = []
|
9
|
+
options[:verbose] = false
|
10
|
+
options[:line_numbers] = false
|
11
|
+
options[:new_line] = true
|
12
|
+
options[:json] = false
|
13
|
+
options[:pretty_json] = false
|
14
|
+
options[:notifications] = false
|
15
|
+
options[:tracking] = false
|
16
|
+
options[:tracking_file] = './snoopit_db.json'
|
17
|
+
|
18
|
+
OptionParser.new do |opts|
|
19
|
+
|
20
|
+
opts.banner = 'Usage: snoopit [options]'
|
21
|
+
|
22
|
+
opts.on('-s', '--snoopers snoopies.json', 'File contains one or more regular expressions to locate a line of interest in a file' ) do |snoopies_file|
|
23
|
+
options[:snoopies_file] = snoopies_file
|
24
|
+
end
|
25
|
+
|
26
|
+
opts.on('-S', '--snooper snooper_name', 'Only use the named snooper. This option can be used more than once to use several snoopers.' ) do |snooper|
|
27
|
+
options[:snoopies] << snooper
|
28
|
+
end
|
29
|
+
|
30
|
+
opts.on('-t', '--template', 'Generate a template snoopies.json file to stdout' ) do
|
31
|
+
template = File.expand_path '../../support/snoopies.json', __FILE__
|
32
|
+
File.readlines(template).each do |line|
|
33
|
+
puts line
|
34
|
+
end
|
35
|
+
exit
|
36
|
+
end
|
37
|
+
|
38
|
+
opts.on('-T', '--tracking', 'Enable log file tracking using file ./snoopit_db.json') do
|
39
|
+
options[:tracking] = true
|
40
|
+
end
|
41
|
+
|
42
|
+
opts.on('-f', '--tracking-file file_name', 'Specify a different tracking file name and location instead of the default ./snoopit_db.json') do |tracking_file|
|
43
|
+
options[:tracking] = true
|
44
|
+
options[:tracking_file] = tracking_file
|
45
|
+
end
|
46
|
+
|
47
|
+
opts.on('-j', '--json', 'Generate output in json' ) do
|
48
|
+
options[:json] = true
|
49
|
+
end
|
50
|
+
|
51
|
+
opts.on('-J', '--pretty-json', 'Generate output in pretty json' ) do
|
52
|
+
options[:json] = true
|
53
|
+
options[:pretty_json] = true
|
54
|
+
end
|
55
|
+
|
56
|
+
opts.on('-N', '--no-newline', 'Do not output new line between found items' ) do |l|
|
57
|
+
options[:new_line] = false
|
58
|
+
end
|
59
|
+
|
60
|
+
opts.on('-n', '--enable-notifications', 'Enable notifications' ) do |n|
|
61
|
+
options[:notifications] = true
|
62
|
+
end
|
63
|
+
|
64
|
+
opts.on('-l', '--line-numbers', 'show line numbers' ) do |l|
|
65
|
+
options[:line_numbers] = true
|
66
|
+
end
|
67
|
+
|
68
|
+
opts.on_tail('-v', '--verbose', 'prints out file name, matched line number') do
|
69
|
+
options[:verbose] = true
|
70
|
+
end
|
71
|
+
|
72
|
+
opts.on_tail('-h', '--help') do
|
73
|
+
puts opts
|
74
|
+
exit
|
75
|
+
end
|
76
|
+
|
77
|
+
end.parse!
|
78
|
+
|
79
|
+
def verbose(snoopy_name, sniffed)
|
80
|
+
puts ''
|
81
|
+
puts "File: #{sniffed.file}"
|
82
|
+
puts "Snooper: #{snoopy_name}"
|
83
|
+
puts "Match at line number: #{sniffed.line_no}"
|
84
|
+
puts "** Sniffed Out ** #{sniffed.match}"
|
85
|
+
puts ''
|
86
|
+
end
|
87
|
+
|
88
|
+
def get_start_line_number(sniffed)
|
89
|
+
start = sniffed.line_no - sniffed.after.size
|
90
|
+
start = 1 if start <= 0
|
91
|
+
start
|
92
|
+
end
|
93
|
+
|
94
|
+
def line_before(line_no, sniffed)
|
95
|
+
sniffed.before.register.each do |b|
|
96
|
+
unless b.nil?
|
97
|
+
puts "#{line_no}: #{b}"
|
98
|
+
line_no += 1
|
99
|
+
end
|
100
|
+
end
|
101
|
+
line_no
|
102
|
+
end
|
103
|
+
|
104
|
+
def line_after(line_no, sniffed)
|
105
|
+
sniffed.after.register.each do |a|
|
106
|
+
unless a.nil?
|
107
|
+
puts "#{line_no}: #{a}"
|
108
|
+
line_no += 1
|
109
|
+
end
|
110
|
+
end
|
111
|
+
line_no
|
112
|
+
end
|
113
|
+
|
114
|
+
def line_numbers(sniffed)
|
115
|
+
line_no = get_start_line_number sniffed
|
116
|
+
line_no = line_before line_no, sniffed
|
117
|
+
puts "#{line_no}: #{sniffed.match}"
|
118
|
+
line_no += 1
|
119
|
+
line_after line_no, sniffed
|
120
|
+
end
|
121
|
+
|
122
|
+
def no_line_numbers(sniffed)
|
123
|
+
sniffed.before.register.each {|b| puts b unless b.nil? }
|
124
|
+
puts sniffed.match
|
125
|
+
sniffed.after.register.each {|a| puts a unless a.nil? }
|
126
|
+
end
|
127
|
+
|
128
|
+
def dump_lines(snoopies, newline, line_no, is_verbose)
|
129
|
+
snoopies.each do |snoopie|
|
130
|
+
snoopie.sniffers.each do |sniffer|
|
131
|
+
sniffer.sniffed.each do |sniffed|
|
132
|
+
verbose snoopie.name, sniffed if is_verbose
|
133
|
+
line_no ? line_numbers(sniffed) : no_line_numbers(sniffed)
|
134
|
+
puts '' if newline
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
def dump_json(snoopies, pretty=false)
|
141
|
+
if pretty
|
142
|
+
puts JSON.pretty_generate snoopies
|
143
|
+
else
|
144
|
+
puts snoopies.to_json
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
if options[:tracking]
|
149
|
+
puts "File tracking database: #{options[:tracking_file]}"
|
150
|
+
else
|
151
|
+
options[:tracking_file] = nil
|
152
|
+
end
|
153
|
+
|
154
|
+
unless File.exist? options[:snoopies_file]
|
155
|
+
puts "Snooper json file does not exist: #{options[:snoopies_file]}"
|
156
|
+
exit 1
|
157
|
+
end
|
158
|
+
|
159
|
+
#debug_file = File.open(File.expand_path('../../tmp/debug.log', __FILE__), 'w+')
|
160
|
+
#snooper = Snoopit::Snooper.new options[:notifications], options[:tracking_file], debug_file, ::Logger::DEBUG
|
161
|
+
#snooper = Snoopit::Snooper.new options[:notifications], options[:tracking_file], STDOUT, ::Logger::DEBUG
|
162
|
+
snooper = Snoopit::Snooper.new options[:notifications], options[:tracking_file]
|
163
|
+
snooper.load_file options[:snoopies_file]
|
164
|
+
snoopies = snooper.snoop options[:snoopies]
|
165
|
+
if options[:json]
|
166
|
+
dump_json snoopies, options[:pretty_json]
|
167
|
+
else
|
168
|
+
dump_lines snoopies, options[:new_line], options[:line_numbers], options[:verbose]
|
169
|
+
end
|
170
|
+
|
171
|
+
|
172
|
+
|
173
|
+
|
data/lib/snoopit.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'snoopit/version'
|
2
|
+
require 'snoopit/logger'
|
3
|
+
require 'snoopit/register'
|
4
|
+
require 'snoopit/snooper'
|
5
|
+
require 'snoopit/snoopy'
|
6
|
+
require 'snoopit/sniffer'
|
7
|
+
require 'snoopit/detected'
|
8
|
+
require 'snoopit/file_info'
|
9
|
+
require 'snoopit/file_tracker'
|
10
|
+
require 'snoopit/notification_manager'
|
11
|
+
require 'snoopit/notifier'
|
12
|
+
require 'snoopit/notifiers/email'
|
13
|
+
require 'snoopit/notifiers/http'
|
14
|
+
require 'snoopit/notifiers/https'
|
15
|
+
|
16
|
+
module Snoopit
|
17
|
+
|
18
|
+
def self.logger
|
19
|
+
Snoopit::Logging.logger
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Snoopit
|
2
|
+
class Detected
|
3
|
+
|
4
|
+
attr :comment, :before, :after, :after_count, :regexp, :match, :finished, :file, :line_no
|
5
|
+
|
6
|
+
def initialize(comment, pre_before, after, match, file, line_no)
|
7
|
+
setup_before pre_before
|
8
|
+
@comment = comment
|
9
|
+
@after_count = 0
|
10
|
+
@after = Register.new(after)
|
11
|
+
@match = match
|
12
|
+
@file = file
|
13
|
+
@line_no = line_no
|
14
|
+
@finished = false
|
15
|
+
end
|
16
|
+
|
17
|
+
def setup_before(pre_before)
|
18
|
+
@before = Register.new pre_before.size, pre_before.register
|
19
|
+
end
|
20
|
+
|
21
|
+
def track(line)
|
22
|
+
return if line == @match
|
23
|
+
if @after_count < @after.size
|
24
|
+
@after.push_front line
|
25
|
+
@after_count += 1
|
26
|
+
else
|
27
|
+
@finished = true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def finished?
|
32
|
+
@finished
|
33
|
+
end
|
34
|
+
|
35
|
+
def as_json(options=nil)
|
36
|
+
{
|
37
|
+
after: @after,
|
38
|
+
match: @match,
|
39
|
+
before: @before,
|
40
|
+
file: @file,
|
41
|
+
line_no: @line_no
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_json(*a)
|
46
|
+
as_json.to_json(*a)
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
3
|
+
module Snoopit
|
4
|
+
class FileInfo
|
5
|
+
|
6
|
+
attr_accessor :file, :line_no, :offset, :size, :mtime, :last_line, :init_stat
|
7
|
+
|
8
|
+
def initialize(file=nil)
|
9
|
+
@file = file
|
10
|
+
@line_no = 0
|
11
|
+
@offset = 0
|
12
|
+
@size = 0
|
13
|
+
@mtime = nil
|
14
|
+
@last_line = nil
|
15
|
+
@init_stat = true
|
16
|
+
end
|
17
|
+
|
18
|
+
# Update file Info if the file has changed use the file handle to move the file pointer
|
19
|
+
# to the character where reading will start
|
20
|
+
#
|
21
|
+
# @return [boolean] true if updated false not updated
|
22
|
+
def updated?(file_handle)
|
23
|
+
c_stat = File.stat @file
|
24
|
+
if (c_stat.size == @size) && (c_stat.mtime.to_i == @mtime.to_i) && (! @init_stat)
|
25
|
+
Snoopit.logger.debug 'FileTracker.updated? file has not changed: ' + @file
|
26
|
+
updated = false
|
27
|
+
elsif c_stat.size < @size
|
28
|
+
Snoopit.logger.debug 'FileTracker.updated? file size is smaller it is a new new file: ' + @file
|
29
|
+
updated = new_file? file_handle, c_stat
|
30
|
+
elsif (c_stat.size == @size) && (! @mtime.nil?) && (c_stat.mtime.to_i > @mtime.to_i)
|
31
|
+
Snoopit.logger.debug 'FileTracker.updated? file size is same but file time is newer it is a new file: ' + @file
|
32
|
+
updated = new_file? file_handle, c_stat
|
33
|
+
else
|
34
|
+
Snoopit.logger.debug 'FileTracker.updated? reading from last read location: ' + @file
|
35
|
+
updated = read_from_last? file_handle, c_stat
|
36
|
+
end
|
37
|
+
@init_stat = false
|
38
|
+
updated
|
39
|
+
end
|
40
|
+
|
41
|
+
def new_file?(file_handle, stat)
|
42
|
+
# seek to 0
|
43
|
+
Snoopit.logger.debug 'FileTracker.updated? file new read from start of file: ' + @file
|
44
|
+
@offset = 0
|
45
|
+
@size = stat.size
|
46
|
+
@mtime = stat.mtime
|
47
|
+
@last_line = nil
|
48
|
+
file_handle.seek 0, IO::SEEK_SET
|
49
|
+
true
|
50
|
+
end
|
51
|
+
|
52
|
+
def read_from_last?(file_handle, stat)
|
53
|
+
# seek to last position + 1
|
54
|
+
old_size = @size
|
55
|
+
@size = stat.size
|
56
|
+
@mtime = stat.mtime
|
57
|
+
Snoopit.logger.debug "File pointer at byte: #{file_handle.tell}"
|
58
|
+
file_handle.seek old_size, IO::SEEK_SET
|
59
|
+
Snoopit.logger.debug "Starting read from byte: #{file_handle.tell} destination byte #{old_size} new size #{@size}"
|
60
|
+
true
|
61
|
+
end
|
62
|
+
|
63
|
+
def get_last_line(file_handle)
|
64
|
+
line = nil
|
65
|
+
unless @last_line.nil?
|
66
|
+
Snoopit.logger.debug "File point at byte: #{file_handle.tell}"
|
67
|
+
file_handle.seek (-@last_line.bytesize), IO::SEEK_END
|
68
|
+
Snoopit.logger.debug "Seeked to byte: #{file_handle.tell}"
|
69
|
+
line = file_handle.readline
|
70
|
+
end
|
71
|
+
line
|
72
|
+
end
|
73
|
+
|
74
|
+
def as_json(*)
|
75
|
+
{
|
76
|
+
file: @file,
|
77
|
+
line_no: @line_no,
|
78
|
+
offset: @offset,
|
79
|
+
size: @size,
|
80
|
+
mtime: @mtime.iso8601,
|
81
|
+
last_line: @last_line
|
82
|
+
}
|
83
|
+
end
|
84
|
+
|
85
|
+
def to_json(*args)
|
86
|
+
as_json.to_json(*args)
|
87
|
+
end
|
88
|
+
|
89
|
+
def from_json(json_str)
|
90
|
+
from_hash JSON.parse(json_str)
|
91
|
+
end
|
92
|
+
|
93
|
+
def from_hash(hash)
|
94
|
+
@file = hash['file']
|
95
|
+
@line_no = hash['line_no']
|
96
|
+
@offset = hash['offset']
|
97
|
+
@size = hash['size']
|
98
|
+
@mtime = Time.parse hash['mtime']
|
99
|
+
@last_line = hash['last_line']
|
100
|
+
@init_stat = false
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Snoopit
|
2
|
+
class FileTracker
|
3
|
+
|
4
|
+
attr_accessor :files, :db_file
|
5
|
+
|
6
|
+
def initialize(db_file='./snooper_db.json', remove=false)
|
7
|
+
@files = { }
|
8
|
+
@db_file = db_file
|
9
|
+
if remove
|
10
|
+
remove_db @db_file
|
11
|
+
else
|
12
|
+
load_db @db_file
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def foreach(file, &block)
|
17
|
+
file_info = get_file(file)
|
18
|
+
unless file_info.nil?
|
19
|
+
read_lines(file_info, block)
|
20
|
+
save_db @db_file
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def get_file(file)
|
25
|
+
return nil unless File.exist? file
|
26
|
+
@files[file] = FileInfo.new(file) if @files[file].nil?
|
27
|
+
@files[file]
|
28
|
+
end
|
29
|
+
|
30
|
+
def read_lines(file_info, block)
|
31
|
+
begin
|
32
|
+
fh = File.new(file_info.file)
|
33
|
+
if file_info.updated? fh
|
34
|
+
fh.each_line do |line|
|
35
|
+
file_info.offset += line.bytesize
|
36
|
+
file_info.line_no += 1
|
37
|
+
file_info.last_line = line
|
38
|
+
block.call line, file_info.line_no
|
39
|
+
end
|
40
|
+
end
|
41
|
+
ensure
|
42
|
+
fh.close
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def load_db(db_file)
|
47
|
+
if (! db_file.nil?) && (File.exist? db_file)
|
48
|
+
Snoopit.logger.debug 'Loading from db file: ' + db_file
|
49
|
+
hash_db = JSON.parse(IO.read db_file)
|
50
|
+
hash_db.each do |key, file_info|
|
51
|
+
unless file_info.nil?
|
52
|
+
fi = FileInfo.new
|
53
|
+
fi.from_hash(file_info)
|
54
|
+
@files[key] = fi
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def save_db(db_file)
|
61
|
+
unless db_file.nil?
|
62
|
+
File.open db_file, 'w' do |f|
|
63
|
+
f.write @files.to_json
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def remove_db(db_file)
|
69
|
+
if (! db_file.nil?) && (File.exist? db_file)
|
70
|
+
File.delete db_file
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def as_json(*)
|
75
|
+
{ files: @files }
|
76
|
+
end
|
77
|
+
|
78
|
+
def to_json(*args)
|
79
|
+
as_json.to_json(*args)
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'awesome_print'
|
3
|
+
|
4
|
+
module Snoopit
|
5
|
+
module Logging
|
6
|
+
|
7
|
+
LEVEL_MAP = {
|
8
|
+
info: ::Logger::INFO,
|
9
|
+
warn: ::Logger::WARN,
|
10
|
+
error: ::Logger::ERROR,
|
11
|
+
fatal: ::Logger::FATAL,
|
12
|
+
debug: ::Logger::DEBUG
|
13
|
+
}
|
14
|
+
|
15
|
+
def self.create_logger(out=STDOUT, level=::Logger::INFO)
|
16
|
+
@logger = ::Logger.new(out)
|
17
|
+
@logger.level = level
|
18
|
+
@logger
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.logger
|
22
|
+
@logger || create_logger
|
23
|
+
end
|
24
|
+
|
25
|
+
def logger
|
26
|
+
Snoopit::Logging.logger
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|