rails-log-sorter 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/bin/rails-log-sorter +17 -0
- data/lib/rails-log-sorter.rb +93 -0
- data/test/example.log +12 -0
- data/test/test_helper.rb +3 -0
- data/test/test_line.rb +50 -0
- data/test/test_log.rb +32 -0
- metadata +57 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "rails-log-sorter"
|
4
|
+
|
5
|
+
file = ARGV[0]
|
6
|
+
action = ARGV[1]
|
7
|
+
limit = (ARGV[2] || 0).to_f
|
8
|
+
|
9
|
+
log = RailsLogSorter::Log.new_with_file(file)
|
10
|
+
|
11
|
+
reqs = log.find_slowest(action, limit)
|
12
|
+
reqs.each {|req|
|
13
|
+
req.lines.each {|line| puts line.original_line}
|
14
|
+
puts ""
|
15
|
+
puts "*" * 50
|
16
|
+
puts ""
|
17
|
+
}
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module RailsLogSorter
|
2
|
+
class Log
|
3
|
+
def self.new_with_file(file)
|
4
|
+
new(File.read(file))
|
5
|
+
end
|
6
|
+
|
7
|
+
attr_reader :requests
|
8
|
+
|
9
|
+
def initialize(log)
|
10
|
+
@requests = []
|
11
|
+
|
12
|
+
current_requests = {}
|
13
|
+
|
14
|
+
log.split("\n").each {|line|
|
15
|
+
begin
|
16
|
+
line = Line.new(line)
|
17
|
+
|
18
|
+
request = current_requests[line.pid]
|
19
|
+
|
20
|
+
case line.type
|
21
|
+
when :started
|
22
|
+
current_requests[line.pid] = request = Request.new
|
23
|
+
request.lines << line
|
24
|
+
@requests << request
|
25
|
+
when :processing
|
26
|
+
if request
|
27
|
+
request.action = line.action
|
28
|
+
request.lines << line
|
29
|
+
end
|
30
|
+
when :completed
|
31
|
+
if request
|
32
|
+
request.runtime = line.runtime
|
33
|
+
request.lines << line
|
34
|
+
end
|
35
|
+
current_requests[line.pid] = nil
|
36
|
+
else
|
37
|
+
request.lines << line if request
|
38
|
+
end
|
39
|
+
|
40
|
+
rescue
|
41
|
+
p line
|
42
|
+
raise
|
43
|
+
end
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
def find(action)
|
48
|
+
@requests.select {|r| r.action == action}
|
49
|
+
end
|
50
|
+
|
51
|
+
def find_slowest(action, limit=5)
|
52
|
+
find(action).sort_by {|r| r.runtime}.reverse[0..(limit - 1)]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class Request
|
57
|
+
attr_accessor :lines, :action, :runtime
|
58
|
+
|
59
|
+
def initialize
|
60
|
+
@lines = []
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class Line
|
65
|
+
FORMAT = /\A(?<timestamp>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}) \[(?<loglevel>.*)\] (?<message>.*) \(pid:(?<pid>\d+)\)/
|
66
|
+
|
67
|
+
attr_reader :message, :pid, :type, :action, :runtime, :original_line
|
68
|
+
|
69
|
+
def initialize(line)
|
70
|
+
@original_line = line
|
71
|
+
match = line.match(FORMAT)
|
72
|
+
if !match
|
73
|
+
@type = :unknown
|
74
|
+
else
|
75
|
+
@message = match[:message]
|
76
|
+
@pid = match[:pid]
|
77
|
+
|
78
|
+
@type = case @message
|
79
|
+
when /\AStarted (GET|POST|PUT|DELETE)/
|
80
|
+
:started
|
81
|
+
when /\AProcessing by (\S+) as/
|
82
|
+
@action = $1
|
83
|
+
:processing
|
84
|
+
when /Completed \d{3} .* in (\d+\.\d+)ms/
|
85
|
+
@runtime = $1.to_f
|
86
|
+
:completed
|
87
|
+
else
|
88
|
+
:log
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
data/test/example.log
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
2014-03-04 10:00:00.100 [INFO ] Started GET "/" for 127.0.0.1 at 2014-03-04 10:26:45 +0100 (pid:1)
|
2
|
+
2014-03-04 10:00:00.100 [INFO ] Started GET "/123" for 127.0.0.1 at 2014-03-04 10:26:45 +0100 (pid:2)
|
3
|
+
2014-03-04 10:00:00.200 [INFO ] Processing by BlogController#index as HTML (pid:1)
|
4
|
+
2014-03-04 10:00:00.200 [INFO ] Processing by BlogController#show as HTML (pid:2)
|
5
|
+
2014-03-04 10:00:00.300 [DEBUG ] We are probably serving up some blogs now. (pid:1)
|
6
|
+
2014-03-04 10:00:00.300 [DEBUG ] Serving the single blog alright (pid:2)
|
7
|
+
2014-03-04 10:00:00.400 [INFO ] Completed 200 OK in 100.2ms (pid:1)
|
8
|
+
2014-03-04 10:00:00.400 [INFO ] Completed 200 OK in 200.18ms (pid:2)
|
9
|
+
2014-03-04 10:00:00.100 [INFO ] Started GET "/" for 127.0.0.1 at 2014-03-04 10:26:45 +0100 (pid:1)
|
10
|
+
2014-03-04 10:00:00.200 [INFO ] Processing by BlogController#index as HTML (pid:1)
|
11
|
+
2014-03-04 10:00:00.300 [DEBUG ] We are probably serving up some blogs now. (pid:1)
|
12
|
+
2014-03-04 10:00:00.400 [INFO ] Completed 200 OK in 300.3ms (pid:1)
|
data/test/test_helper.rb
ADDED
data/test/test_line.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class TestLine < MiniTest::Unit::TestCase
|
4
|
+
include RailsLogSorter
|
5
|
+
|
6
|
+
def test_started_line
|
7
|
+
line = Line.new('2014-03-04 10:00:00.100 [INFO ] Started GET "/" (pid:1)')
|
8
|
+
|
9
|
+
assert_equal 'Started GET "/"', line.message
|
10
|
+
assert_equal '1', line.pid
|
11
|
+
assert_equal :started, line.type
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_processing_line
|
15
|
+
line = Line.new('2014-03-04 10:00:00.100 [INFO ] Processing by FooController#index as HTML (pid:1)')
|
16
|
+
|
17
|
+
assert_equal '1', line.pid
|
18
|
+
assert_equal :processing, line.type
|
19
|
+
assert_equal "FooController#index", line.action
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_completed_line
|
23
|
+
line = Line.new('2014-03-04 10:00:00.100 [INFO ] Completed 200 OK in 50.1ms (pid:1)')
|
24
|
+
|
25
|
+
assert_equal '1', line.pid
|
26
|
+
assert_equal :completed, line.type
|
27
|
+
assert_equal 50.1, line.runtime
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_completed_line_with_breakdown
|
31
|
+
line = Line.new('2014-03-04 10:26:47.736 [INFO ] Completed 200 OK in 1837.5ms (Views: 1808.8ms | ActiveRecord: 5.1ms) (pid:18269)')
|
32
|
+
|
33
|
+
assert_equal '18269', line.pid
|
34
|
+
assert_equal :completed, line.type
|
35
|
+
assert_equal 1837.5, line.runtime
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_log_line
|
39
|
+
line = Line.new('2014-03-04 10:00:00.100 [INFO ] This is a log (pid:1)')
|
40
|
+
|
41
|
+
assert_equal '1', line.pid
|
42
|
+
assert_equal :log, line.type
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_unknown_line
|
46
|
+
line = Line.new('Connecting to database')
|
47
|
+
|
48
|
+
assert_equal :unknown, line.type
|
49
|
+
end
|
50
|
+
end
|
data/test/test_log.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class TestLog < MiniTest::Unit::TestCase
|
4
|
+
include RailsLogSorter
|
5
|
+
|
6
|
+
def test_parsing_log_file
|
7
|
+
log = Log.new_with_file("test/example.log")
|
8
|
+
|
9
|
+
r1 = log.requests[0]
|
10
|
+
assert_equal "BlogController#index", r1.action
|
11
|
+
assert_equal 100.2, r1.runtime
|
12
|
+
|
13
|
+
r2 = log.requests[1]
|
14
|
+
assert_equal "BlogController#show", r2.action
|
15
|
+
assert_equal 200.18, r2.runtime
|
16
|
+
|
17
|
+
r3 = log.requests[2]
|
18
|
+
assert_equal "BlogController#index", r3.action
|
19
|
+
assert_equal 300.3, r3.runtime
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_find_slowest
|
23
|
+
log = Log.new_with_file("test/example.log")
|
24
|
+
|
25
|
+
slow = log.find_slowest("BlogController#index")
|
26
|
+
|
27
|
+
assert_equal 2, slow.size
|
28
|
+
|
29
|
+
assert_equal 300.3, slow[0].runtime
|
30
|
+
assert_equal 100.2, slow[1].runtime
|
31
|
+
end
|
32
|
+
end
|
metadata
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rails-log-sorter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Harry Vangberg
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-04-15 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: Sort log files
|
15
|
+
email:
|
16
|
+
- hv@firmafon.dk
|
17
|
+
executables:
|
18
|
+
- rails-log-sorter
|
19
|
+
extensions: []
|
20
|
+
extra_rdoc_files: []
|
21
|
+
files:
|
22
|
+
- lib/rails-log-sorter.rb
|
23
|
+
- test/test_helper.rb
|
24
|
+
- test/test_log.rb
|
25
|
+
- test/example.log
|
26
|
+
- test/test_line.rb
|
27
|
+
- bin/rails-log-sorter
|
28
|
+
homepage: https://github.com/firmafon/rails-log-sorter
|
29
|
+
licenses: []
|
30
|
+
post_install_message:
|
31
|
+
rdoc_options: []
|
32
|
+
require_paths:
|
33
|
+
- lib
|
34
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
35
|
+
none: false
|
36
|
+
requirements:
|
37
|
+
- - ! '>='
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
requirements: []
|
47
|
+
rubyforge_project:
|
48
|
+
rubygems_version: 1.8.23
|
49
|
+
signing_key:
|
50
|
+
specification_version: 3
|
51
|
+
summary: Sort log files
|
52
|
+
test_files:
|
53
|
+
- test/test_helper.rb
|
54
|
+
- test/test_log.rb
|
55
|
+
- test/example.log
|
56
|
+
- test/test_line.rb
|
57
|
+
has_rdoc:
|