logtool 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1 @@
1
+ gemspec
@@ -0,0 +1,4 @@
1
+ rlog
2
+ ====
3
+
4
+ LogTool parsing utility for Rails logs
@@ -0,0 +1 @@
1
+ require 'rake/testtask'
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'logtool'
4
+ LogTool::execute
5
+
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.join(File.dirname(File.expand_path(__FILE__)), "logtool", "block.rb")
4
+ require File.join(File.dirname(File.expand_path(__FILE__)), "logtool", "parser.rb")
5
+ require File.join(File.dirname(File.expand_path(__FILE__)), "logtool", "query.rb")
6
+
7
+ module LogTool
8
+ def self.usage
9
+ <<USAGE
10
+ Usage: logtool file mode mode_options
11
+
12
+ File is the rails log file that should be parsed. Mode can be either gtk or query. For gtk, a window will open and let you explore the log file. For query, you can write a query that will be executed on the log entries.
13
+
14
+ == Query mode ==
15
+ When using the query mode, mode_options should be in the following format: filter information [options]. The first parameter filters the log entries, the second one is the information you want to retrieve. The last one is the only optional one, where you can specify further options.
16
+ ==filter==
17
+ Boolean Operators: and, or, not
18
+ Value Operators ==, <=, >=, <, >
19
+ Values: ip, method, response, asset
20
+
21
+ ==information to retrieve==
22
+ ip, head, tail, method, time
23
+
24
+ ==options==
25
+ -l num: shows only the last num entries.
26
+
27
+ Examples:
28
+ logtool log/production.log query "ip == 127.0.0.1" head
29
+ logtool log/production.log query "(ip == 127.0.0.1) and not asset" head
30
+ logtool log/production.log query "(ip == 127.0.0.1) or (ip == 0.0.0.0) and not asset" head
31
+ logtool log/production.log query "response > 200" ip
32
+ USAGE
33
+ end
34
+
35
+ def self.fatal_error(err_msg=nil)
36
+ puts err_msg if err_msg
37
+ puts usage
38
+ exit 1
39
+ end
40
+
41
+ def self.supported_modes
42
+ %W(query)
43
+ end
44
+
45
+ def self.parse_args
46
+ fatal_error if ARGV.size < 2
47
+
48
+ options = {}
49
+ options[:input_file] = ARGV[0]
50
+ options[:mode] = ARGV[1]
51
+ options[:mode_args] = ARGV[2..-1]
52
+
53
+ fatal_error("Invalid mode") unless self.supported_modes.include?(options[:mode])
54
+
55
+ return options
56
+ end
57
+
58
+ public
59
+ def self.execute
60
+ options = parse_args
61
+ blocks = Parser.parse_blocks(options[:input_file])
62
+
63
+ case options[:mode]
64
+ when 'query' then
65
+ query = Query.new(blocks, options)
66
+ query.run
67
+ else puts "Mode not implemented"; exit 1
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,44 @@
1
+ module LogTool
2
+ class Block
3
+ attr_accessor :text, :ip, :method, :response, :asset, :time
4
+
5
+ def to_s
6
+ @text
7
+ end
8
+
9
+ def is_asset?
10
+ @asset
11
+ end
12
+
13
+ def head
14
+ @text.split("\n").first
15
+ end
16
+
17
+ def tail
18
+ @text.split("\n").last
19
+ end
20
+
21
+ def self.head
22
+ #$1 = method, $2 = IP
23
+ /^Started (GET|POST|PUT|DELETE) ".+?" for (\d\d?\d?.\d\d?\d?.\d\d?\d?.\d\d?\d?) at .*$/
24
+ end
25
+
26
+ def self.tail
27
+ # $1 = response
28
+ time = /\d+(ms|s)/
29
+ /^Completed (\d\d\d) .+? in (\d+ms)$/
30
+ end
31
+
32
+ def self.asset_tail
33
+ # $1 = response, $2 = time
34
+ time = /\d+(ms|s)/
35
+ /^Served asset [0-9a-zA-Z\/._]+ - (\d\d\d) .*? \((\d+ms)\)$/
36
+ end
37
+
38
+ def test_for(query)
39
+ eval query
40
+ rescue => e
41
+ LogTool::fatal_error("Bad filter. #{e.message}")
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,45 @@
1
+ require File.join(File.dirname(File.expand_path(__FILE__)), "block.rb")
2
+
3
+ module LogTool
4
+ class Parser
5
+
6
+ def self.open_log(file_path)
7
+ yield File.open(file_path)
8
+ rescue Errno::ENOENT
9
+ puts "#{file_path}: No such file or directory"
10
+ exit 1
11
+ rescue Errno::EACCES
12
+ puts "#{file_path}: Permission denied"
13
+ exit 2
14
+ end
15
+
16
+ def self.parse_blocks(file_path)
17
+ blocks = []
18
+ block = nil
19
+ open_log(file_path) do |fh|
20
+ fh.read.split("\n").each do |line|
21
+ if line =~ Block.head
22
+ blocks << block if block
23
+ block = Block.new
24
+ block.method = $1
25
+ block.ip = $2
26
+ block.text = line + "\n"
27
+ elsif block and line =~ Block.tail
28
+ block.response = $1
29
+ block.time = $2
30
+ block.text += line + "\n"
31
+ block.freeze
32
+ elsif block and line =~ Block.asset_tail
33
+ block.response = $1
34
+ block.time = $2
35
+ block.text += line + "\n"
36
+ block.freeze
37
+ else
38
+ block.text += line + "\n" if block and !block.frozen?
39
+ end
40
+ end
41
+ end
42
+ return blocks
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,50 @@
1
+ module LogTool
2
+ class Query
3
+ def initialize(blocks, options)
4
+ @blocks = blocks
5
+ @filter = parse_filter(options[:mode_args])
6
+ @target_data = parse_target_data(options[:mode_args])
7
+ end
8
+
9
+ def target_data_type
10
+ %W(ip head tail method time)
11
+ end
12
+
13
+ def parse_target_data(args)
14
+ if target_data_type.include?(args[1])
15
+ args[1]
16
+ else
17
+ LogTool::fatal_error("Undefined target data type: #{args[1]}")
18
+ end
19
+ end
20
+
21
+ def keywords
22
+ target_data_type + %W(ip and or not < > <= >= == != asset)
23
+ end
24
+
25
+ def ip_regexp
26
+ /\d\d?\d?\.\d\d?\d?\.\d\d?\d?\.\d\d?\d?/
27
+ end
28
+
29
+ def parse_filter(options)
30
+ filter = []
31
+ options.first.split.each do |word|
32
+ if keywords.include?(word)
33
+ filter << word
34
+ elsif word =~ ip_regexp
35
+ filter << "\"#{word}\""
36
+ elsif word =~ /\d+/
37
+ filter << word
38
+ else
39
+ LogTool::fatal_error("Invalid filter keyword: #{word}")
40
+ end
41
+ end
42
+ filter.join(' ')
43
+ end
44
+
45
+ def run
46
+ @blocks.find_all{|b| b.test_for(@filter)}.each{|b| puts b.send(@target_data)}
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,18 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "logtool"
5
+ s.version = "0.0.1"
6
+ s.platform = Gem::Platform::RUBY
7
+ s.authors = ["Felipe Tanus"]
8
+ s.email = ["fotanus@jgmail.com"]
9
+ s.homepage = "https://github.com/fotanus/logtool"
10
+ s.summary = %q{logtool is a Rails log parser}
11
+ s.description = %q{logtool is a Rails log parser that can accepts complex queries}
12
+ s.files = `git ls-files`.split("\n")
13
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
14
+ s.require_paths = ["lib"]
15
+ s.extra_rdoc_files = [
16
+ "README.md"
17
+ ]
18
+ end
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: logtool
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
+ - Felipe Tanus
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2013-01-17 00:00:00 -02:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: logtool is a Rails log parser that can accepts complex queries
23
+ email:
24
+ - fotanus@jgmail.com
25
+ executables:
26
+ - logtool
27
+ extensions: []
28
+
29
+ extra_rdoc_files:
30
+ - README.md
31
+ files:
32
+ - .gitignore
33
+ - Gemfile
34
+ - README.md
35
+ - Rakefile
36
+ - bin/logtool
37
+ - lib/logtool.rb
38
+ - lib/logtool/block.rb
39
+ - lib/logtool/parser.rb
40
+ - lib/logtool/query.rb
41
+ - logtool.gemspec
42
+ has_rdoc: true
43
+ homepage: https://github.com/fotanus/logtool
44
+ licenses: []
45
+
46
+ post_install_message:
47
+ rdoc_options: []
48
+
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ hash: 3
57
+ segments:
58
+ - 0
59
+ version: "0"
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ hash: 3
66
+ segments:
67
+ - 0
68
+ version: "0"
69
+ requirements: []
70
+
71
+ rubyforge_project:
72
+ rubygems_version: 1.6.2
73
+ signing_key:
74
+ specification_version: 3
75
+ summary: logtool is a Rails log parser
76
+ test_files: []
77
+