plog 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +2 -141
- data/Rakefile +6 -0
- data/VERSION +1 -1
- data/lib/plog.rb +7 -12
- data/lib/plog/parser.rb +31 -0
- data/lib/plog/record_abstraction.rb +62 -0
- data/lib/plog/scanner.rb +33 -0
- data/plog.gemspec +25 -36
- metadata +32 -24
- data/.gitignore +0 -7
- data/lib/cli.rb +0 -119
- data/lib/completed_line.rb +0 -109
- data/lib/log_file.rb +0 -52
- data/lib/object_file.rb +0 -165
- data/lib/url.rb +0 -37
data/README.rdoc
CHANGED
@@ -1,148 +1,9 @@
|
|
1
1
|
= Plog
|
2
2
|
|
3
|
-
|
3
|
+
Production log parser.
|
4
4
|
|
5
|
-
by Kazuyoshi Tlacaelel.
|
6
5
|
|
7
|
-
|
8
|
-
$ ls plog/
|
9
|
-
objects statistics.txt
|
10
|
-
|
11
|
-
|
12
|
-
== Copyright
|
13
|
-
|
14
|
-
Copyright (c) 2009 Kazuyoshi Tlacaelel. See LICENSE for details.
|
15
|
-
|
16
|
-
== Comming soon!
|
17
|
-
|
18
|
-
comming soon to a terminal near you!
|
19
|
-
|
20
|
-
* sorting:
|
21
|
-
* more statistics
|
22
|
-
(hits, number of hits per response type like 200, 302 etc)
|
23
|
-
|
24
|
-
May be... but don't expect any of these to become a reality
|
25
|
-
I love programming and tons of ideas flow in my head with posibilities
|
26
|
-
but I do not have the time! :(
|
27
|
-
|
28
|
-
* Different export drivers?
|
29
|
-
(should be another plugin)
|
30
|
-
|
31
|
-
* cli options abstracted into classes so they can be aggregated with ease
|
32
|
-
(this might be cool)
|
33
|
-
|
34
|
-
* google charts integration
|
35
|
-
(is too much for this api)
|
36
|
-
(may be as a different plugin)
|
37
|
-
|
38
|
-
== Dependancies:
|
39
|
-
|
40
|
-
* FileUtils
|
41
|
-
* MD5
|
42
|
-
* File
|
43
|
-
* Dir
|
44
|
-
* URI
|
45
|
-
* rubygems 1.3.3
|
46
|
-
|
47
|
-
== Features:
|
48
|
-
|
49
|
-
Compiles statistics in a easy to read manner
|
50
|
-
|
51
|
-
|
52
|
-
Hits Time Avg-Time DbTime Avg-DB View Avg-View Url
|
53
|
-
93 18057 0.194 5775 0.062 10437 0.112 /users/[0-9]
|
54
|
-
12 277 0.023 193 0.016 0 0 /user_themes/update
|
55
|
-
1 290 0.29 107 0.107 175 0.175 /posts
|
56
|
-
3 99 0.033 57 0.019 0 0 /comments
|
57
|
-
82 11922 0.145 5969 0.072 4979 0.06 /user/[0-9]/comments
|
58
|
-
1868 1301006 0.696 623246 0.333 491474 0.263 /user/[0-9]/posts?
|
59
|
-
1 72 0.072 39 0.039 0 0 /confirm_destroy_account/[0-9]?
|
60
|
-
797 971882 1.219 414829 0.52 423130 0.53 /user/[0-9]/articles/friends?
|
61
|
-
122 20830 0.17 7081 0.058 10291 0.084 /user/[0-9]/topics?
|
62
|
-
|
63
|
-
== Improvized twicks:
|
64
|
-
|
65
|
-
You can improvize some quick sorting using the command line like:
|
66
|
-
|
67
|
-
cat plog/statistics.txt | head -n1 > some_file.txt
|
68
|
-
cat plog/statistics.txt | sort -nr >> some_file.txt
|
69
|
-
|
70
|
-
This will create a new file with sorted results by hits for you!
|
71
|
-
|
72
|
-
Hits Time Avg-Time DbTime Avg-DB View Avg-View Url
|
73
|
-
1868 1301006 0.696 623246 0.333 491474 0.263 /user/[0-9]/posts?
|
74
|
-
797 971882 1.219 414829 0.52 423130 0.53 /user/[0-9]/articles/friends?
|
75
|
-
122 20830 0.17 7081 0.058 10291 0.084 /user/[0-9]/topics?
|
76
|
-
93 18057 0.194 5775 0.062 10437 0.112 /users/[0-9]
|
77
|
-
82 11922 0.145 5969 0.072 4979 0.06 /user/[0-9]/comments
|
78
|
-
12 277 0.023 193 0.016 0 0 /user_themes/update
|
79
|
-
3 99 0.033 57 0.019 0 0 /comments
|
80
|
-
1 72 0.072 39 0.039 0 0 /confirm_destroy_account/[0-9]?
|
81
|
-
1 290 0.29 107 0.107 175 0.175 /posts
|
82
|
-
|
83
|
-
== Installation
|
84
|
-
|
85
|
-
$ gem sources -a http://gems.github.com
|
86
|
-
$ gem install ktlacaelel-plog
|
87
|
-
|
88
|
-
== Known bugs:
|
89
|
-
none, so far..
|
90
|
-
|
91
|
-
== License:
|
92
|
-
MIT. See the "LICENSE" file for details.
|
93
|
-
|
94
|
-
== Participate
|
95
|
-
|
96
|
-
* Send me a brief message
|
97
|
-
* Fork the project.
|
98
|
-
* Add tests for it. This is important so I don't break it in a
|
99
|
-
future version unintentionally.
|
100
|
-
* Make your feature addition or bug fix.
|
101
|
-
* Commit, do not mess with rakefile, version, or history.
|
102
|
-
(if you want to have your own version, that is fine but
|
103
|
-
bump version in a commit by itself I can ignore when I pull)
|
104
|
-
* Send me a pull request. Bonus points for topic branches.
|
105
|
-
|
106
|
-
== Output:
|
107
|
-
|
108
|
-
All output is packed in a plog directory, from wherever you run
|
109
|
-
the "plog" executable.
|
110
|
-
|
111
|
-
Inside this directory you'll find a directory called objects.
|
112
|
-
wich contains many small files called "object-files"
|
113
|
-
this files contain all the parsed statistics of you log files.
|
114
|
-
|
115
|
-
The information is scattered and can be reused for future compilations
|
116
|
-
meaning that new compilations data will merge with the existing one.
|
117
|
-
|
118
|
-
all data in these "object-files" is read and summarized into a
|
119
|
-
statistics.txt file found inside of the plog directory.
|
120
|
-
|
121
|
-
== Tests:
|
122
|
-
|
123
|
-
* 70% tested, with shoulda
|
124
|
-
* all core areas of the api are tested, and tests pass!
|
125
|
-
* only the cli part of the api has no tests, but will be implemented soon
|
126
|
-
|
127
|
-
== Usage:
|
128
|
-
|
129
|
-
The "plog" ( production log ) executable receives one option.
|
130
|
-
This must be a directory containing one or more production logs.
|
131
|
-
Only logs in the first level will be parsed.
|
132
|
-
Recursive reading is not allowed
|
133
|
-
|
134
|
-
$ plog directory_with_logs/
|
135
|
-
|
136
|
-
== Works on:
|
137
|
-
|
138
|
-
* OSX Darwin utopia.local 9.8.0 Darwin Kernel Version 9.8.0: Wed Jul 15 16:55:01 PDT 2009; root:xnu-1228.15.4~1/RELEASE_I386 i386
|
139
|
-
* UBUNTU Linux utopia.local 2.6.28-11-server #42-Ubuntu SMP Fri Apr 17 02:45:36 UTC 2009 x86_64 GNU/Linux
|
140
|
-
|
141
|
-
* OSX ruby 1.8.6 (2008-08-11 patchlevel 287) [universal-darwin9.0]
|
142
|
-
* UBUNTU ruby 1.8.7 (2009-06-12 patchlevel 174) [x86_64-linux]
|
143
|
-
|
144
|
-
* rubygems 1.3.4
|
145
|
-
* rubygems 1.3.3
|
6
|
+
update coming soon!
|
146
7
|
|
147
8
|
== The end.
|
148
9
|
|
data/Rakefile
CHANGED
@@ -54,3 +54,9 @@ Rake::RDocTask.new do |rdoc|
|
|
54
54
|
rdoc.rdoc_files.include('README*')
|
55
55
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
56
56
|
end
|
57
|
+
|
58
|
+
desc 'Generate code statistics'
|
59
|
+
task :stats do
|
60
|
+
require 'code_statistics'
|
61
|
+
CodeStatistics.new(['Plog', 'lib'], ['Tests', 'test']).to_s
|
62
|
+
end
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.6.0
|
data/lib/plog.rb
CHANGED
@@ -1,13 +1,8 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require '
|
4
|
-
require '
|
5
|
-
require '
|
6
|
-
|
7
|
-
|
8
|
-
require 'cli'
|
9
|
-
require 'completed_line'
|
10
|
-
require 'log_file'
|
11
|
-
require 'object_file'
|
12
|
-
require 'url'
|
1
|
+
require 'yaml'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'active_record'
|
4
|
+
require 'active_support'
|
5
|
+
require 'plog/record_abstraction'
|
6
|
+
require 'plog/scanner'
|
7
|
+
require 'plog/parser'
|
13
8
|
|
data/lib/plog/parser.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
module Plog
|
2
|
+
|
3
|
+
class Parser
|
4
|
+
|
5
|
+
class << Parser
|
6
|
+
|
7
|
+
attr_reader :data
|
8
|
+
|
9
|
+
def reset
|
10
|
+
end
|
11
|
+
|
12
|
+
def crunch data
|
13
|
+
load_data data
|
14
|
+
end
|
15
|
+
|
16
|
+
def load_data(value)
|
17
|
+
reset
|
18
|
+
@data = value
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def is_candidate?
|
23
|
+
raise 'You must implement a is_candidate? method in your tokenizer class'
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Plog
|
2
|
+
|
3
|
+
class RecordAbstraction
|
4
|
+
|
5
|
+
class << RecordAbstraction
|
6
|
+
|
7
|
+
def setup(table)
|
8
|
+
@table = table
|
9
|
+
end
|
10
|
+
|
11
|
+
def migration_name
|
12
|
+
"#{record_name}Migration"
|
13
|
+
end
|
14
|
+
|
15
|
+
def record_name
|
16
|
+
"#{@table[:table_name].to_s.camelize}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def record_class
|
20
|
+
"
|
21
|
+
class ::#{record_name} < ActiveRecord::Base
|
22
|
+
set_table_name :#{record_name.underscore}
|
23
|
+
end
|
24
|
+
"
|
25
|
+
end
|
26
|
+
|
27
|
+
def migration_class
|
28
|
+
"
|
29
|
+
class ::#{migration_name} < ActiveRecord::Migration
|
30
|
+
def self.up
|
31
|
+
create_table :#{@table[:table_name]} do |t|
|
32
|
+
#{@table[:fields].map { |field, type| 't.' + type.to_s + ' :' + field.to_s } * "\n " }
|
33
|
+
t.timestamps
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
"
|
38
|
+
end
|
39
|
+
|
40
|
+
def record_constant
|
41
|
+
eval(record_class)
|
42
|
+
eval('::' + record_name)
|
43
|
+
end
|
44
|
+
|
45
|
+
def migration_constant
|
46
|
+
eval(migration_class)
|
47
|
+
eval('::' + migration_name)
|
48
|
+
end
|
49
|
+
|
50
|
+
def record_exists?
|
51
|
+
ActiveRecord::Base.connection.execute('show tables').each do |record|
|
52
|
+
return true if record.first == @table[:table_name].to_s
|
53
|
+
end
|
54
|
+
false
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
data/lib/plog/scanner.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
module Plog
|
2
|
+
|
3
|
+
class Scanner
|
4
|
+
|
5
|
+
class << Scanner
|
6
|
+
|
7
|
+
def load(io, config, &block)
|
8
|
+
@config = config
|
9
|
+
@config.each do |table|
|
10
|
+
RecordAbstraction.setup(table)
|
11
|
+
unless RecordAbstraction.record_exists?
|
12
|
+
RecordAbstraction.migration_constant.up
|
13
|
+
end
|
14
|
+
end
|
15
|
+
io.each_line { |line| yield(line, self) }
|
16
|
+
end
|
17
|
+
|
18
|
+
def record table_name
|
19
|
+
constant(table_name).new
|
20
|
+
end
|
21
|
+
|
22
|
+
# TODO: optimize
|
23
|
+
def constant table_name
|
24
|
+
config = @config.find { |hash| hash[:table_name] == table_name }
|
25
|
+
RecordAbstraction.setup(config)
|
26
|
+
RecordAbstraction.record_constant
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
data/plog.gemspec
CHANGED
@@ -1,65 +1,53 @@
|
|
1
1
|
# Generated by jeweler
|
2
|
-
# DO NOT EDIT THIS FILE
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{plog}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.6.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Kazuyoshi Tlacaelel"]
|
12
|
-
s.date = %q{
|
12
|
+
s.date = %q{2012-05-09}
|
13
13
|
s.default_executable = %q{plog}
|
14
14
|
s.description = %q{Plog - Ruby on Rails production log statistic generator. by Kazuyoshi Tlacaelel}
|
15
15
|
s.email = %q{kazu.dev@gmail.com}
|
16
16
|
s.executables = ["plog"]
|
17
17
|
s.extra_rdoc_files = [
|
18
18
|
"LICENSE",
|
19
|
-
|
19
|
+
"README.rdoc"
|
20
20
|
]
|
21
21
|
s.files = [
|
22
22
|
".document",
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
"test/plog_test.rb",
|
41
|
-
"test/test_helper.rb",
|
42
|
-
"test/url_test.rb"
|
23
|
+
"LICENSE",
|
24
|
+
"README.rdoc",
|
25
|
+
"Rakefile",
|
26
|
+
"VERSION",
|
27
|
+
"bin/plog",
|
28
|
+
"lib/plog.rb",
|
29
|
+
"lib/plog/parser.rb",
|
30
|
+
"lib/plog/record_abstraction.rb",
|
31
|
+
"lib/plog/scanner.rb",
|
32
|
+
"plog.gemspec",
|
33
|
+
"test/completed_line_test.rb",
|
34
|
+
"test/data/example.log",
|
35
|
+
"test/log_file_test.rb",
|
36
|
+
"test/object_file_test.rb",
|
37
|
+
"test/plog_test.rb",
|
38
|
+
"test/test_helper.rb",
|
39
|
+
"test/url_test.rb"
|
43
40
|
]
|
44
41
|
s.homepage = %q{http://github.com/ktlacaelel/plog}
|
45
|
-
s.rdoc_options = ["--charset=UTF-8"]
|
46
42
|
s.require_paths = ["lib"]
|
47
|
-
s.rubygems_version = %q{1.3.
|
43
|
+
s.rubygems_version = %q{1.3.7}
|
48
44
|
s.summary = %q{Plog - Ruby on Rails production log statistic generator. by Kazuyoshi Tlacaelel}
|
49
|
-
s.test_files = [
|
50
|
-
"test/completed_line_test.rb",
|
51
|
-
"test/log_file_test.rb",
|
52
|
-
"test/object_file_test.rb",
|
53
|
-
"test/plog_test.rb",
|
54
|
-
"test/test_helper.rb",
|
55
|
-
"test/url_test.rb"
|
56
|
-
]
|
57
45
|
|
58
46
|
if s.respond_to? :specification_version then
|
59
47
|
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
60
48
|
s.specification_version = 3
|
61
49
|
|
62
|
-
if Gem::Version.new(Gem::
|
50
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
63
51
|
s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
64
52
|
else
|
65
53
|
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
@@ -68,3 +56,4 @@ Gem::Specification.new do |s|
|
|
68
56
|
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
69
57
|
end
|
70
58
|
end
|
59
|
+
|
metadata
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: plog
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
hash: 7
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 6
|
9
|
+
- 0
|
10
|
+
version: 0.6.0
|
5
11
|
platform: ruby
|
6
12
|
authors:
|
7
13
|
- Kazuyoshi Tlacaelel
|
@@ -9,19 +15,23 @@ autorequire:
|
|
9
15
|
bindir: bin
|
10
16
|
cert_chain: []
|
11
17
|
|
12
|
-
date:
|
18
|
+
date: 2012-05-09 00:00:00 -05:00
|
13
19
|
default_executable: plog
|
14
20
|
dependencies:
|
15
21
|
- !ruby/object:Gem::Dependency
|
16
22
|
name: thoughtbot-shoulda
|
17
|
-
|
18
|
-
|
19
|
-
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
20
26
|
requirements:
|
21
27
|
- - ">="
|
22
28
|
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
23
32
|
version: "0"
|
24
|
-
|
33
|
+
type: :development
|
34
|
+
version_requirements: *id001
|
25
35
|
description: Plog - Ruby on Rails production log statistic generator. by Kazuyoshi Tlacaelel
|
26
36
|
email: kazu.dev@gmail.com
|
27
37
|
executables:
|
@@ -33,18 +43,15 @@ extra_rdoc_files:
|
|
33
43
|
- README.rdoc
|
34
44
|
files:
|
35
45
|
- .document
|
36
|
-
- .gitignore
|
37
46
|
- LICENSE
|
38
47
|
- README.rdoc
|
39
48
|
- Rakefile
|
40
49
|
- VERSION
|
41
50
|
- bin/plog
|
42
|
-
- lib/cli.rb
|
43
|
-
- lib/completed_line.rb
|
44
|
-
- lib/log_file.rb
|
45
|
-
- lib/object_file.rb
|
46
51
|
- lib/plog.rb
|
47
|
-
- lib/
|
52
|
+
- lib/plog/parser.rb
|
53
|
+
- lib/plog/record_abstraction.rb
|
54
|
+
- lib/plog/scanner.rb
|
48
55
|
- plog.gemspec
|
49
56
|
- test/completed_line_test.rb
|
50
57
|
- test/data/example.log
|
@@ -58,33 +65,34 @@ homepage: http://github.com/ktlacaelel/plog
|
|
58
65
|
licenses: []
|
59
66
|
|
60
67
|
post_install_message:
|
61
|
-
rdoc_options:
|
62
|
-
|
68
|
+
rdoc_options: []
|
69
|
+
|
63
70
|
require_paths:
|
64
71
|
- lib
|
65
72
|
required_ruby_version: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
66
74
|
requirements:
|
67
75
|
- - ">="
|
68
76
|
- !ruby/object:Gem::Version
|
77
|
+
hash: 3
|
78
|
+
segments:
|
79
|
+
- 0
|
69
80
|
version: "0"
|
70
|
-
version:
|
71
81
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
82
|
+
none: false
|
72
83
|
requirements:
|
73
84
|
- - ">="
|
74
85
|
- !ruby/object:Gem::Version
|
86
|
+
hash: 3
|
87
|
+
segments:
|
88
|
+
- 0
|
75
89
|
version: "0"
|
76
|
-
version:
|
77
90
|
requirements: []
|
78
91
|
|
79
92
|
rubyforge_project:
|
80
|
-
rubygems_version: 1.3.
|
93
|
+
rubygems_version: 1.3.7
|
81
94
|
signing_key:
|
82
95
|
specification_version: 3
|
83
96
|
summary: Plog - Ruby on Rails production log statistic generator. by Kazuyoshi Tlacaelel
|
84
|
-
test_files:
|
85
|
-
|
86
|
-
- test/log_file_test.rb
|
87
|
-
- test/object_file_test.rb
|
88
|
-
- test/plog_test.rb
|
89
|
-
- test/test_helper.rb
|
90
|
-
- test/url_test.rb
|
97
|
+
test_files: []
|
98
|
+
|
data/.gitignore
DELETED
data/lib/cli.rb
DELETED
@@ -1,119 +0,0 @@
|
|
1
|
-
module Plog
|
2
|
-
|
3
|
-
class Cli
|
4
|
-
|
5
|
-
attr_reader :source_directory, :log_files
|
6
|
-
|
7
|
-
STATS_FILE = 'statistics.txt'
|
8
|
-
|
9
|
-
# ==========================================================================
|
10
|
-
# CLIENT INTERFACE
|
11
|
-
# ==========================================================================
|
12
|
-
|
13
|
-
def initialize(source_directory, target_directory)
|
14
|
-
@log_files = []
|
15
|
-
@source_directory = source_directory
|
16
|
-
@target_directory = target_directory
|
17
|
-
check_directory_consistency!
|
18
|
-
preload_log_files!
|
19
|
-
end
|
20
|
-
|
21
|
-
def run!
|
22
|
-
stdout count_of_logs_banner(@log_files.size)
|
23
|
-
stdout notice_banner
|
24
|
-
create_object_files
|
25
|
-
destroy_old_log_file
|
26
|
-
append_headers_to_log_file
|
27
|
-
parse_object_files
|
28
|
-
end
|
29
|
-
|
30
|
-
# ==========================================================================
|
31
|
-
# INTERNAL INTERFACE
|
32
|
-
# ==========================================================================
|
33
|
-
|
34
|
-
protected
|
35
|
-
|
36
|
-
def trim(string)
|
37
|
-
return '' unless string.is_a? String
|
38
|
-
string.gsub(/^\s+/, '').gsub(/\s+$/, '')
|
39
|
-
end
|
40
|
-
|
41
|
-
def create_object_files
|
42
|
-
@log_files.each do |log_file|
|
43
|
-
stdout parsing_log_file_banner(log_file.name)
|
44
|
-
log_file.parse_completed_lines!
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def parse_object_files
|
49
|
-
Dir.glob(object_file_pattern).each do |object_file|
|
50
|
-
file = File.new(statistic_file_path, 'a+')
|
51
|
-
file.puts trim(ObjectFile.new(object_file).export)
|
52
|
-
file.close
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def object_file_pattern
|
57
|
-
File.join(@target_directory, 'objects') + '/*'
|
58
|
-
end
|
59
|
-
|
60
|
-
def statistic_file_path
|
61
|
-
File.join(@target_directory, STATS_FILE)
|
62
|
-
end
|
63
|
-
|
64
|
-
def destroy_old_log_file
|
65
|
-
FileUtils.touch statistic_file_path
|
66
|
-
FileUtils.rm statistic_file_path
|
67
|
-
end
|
68
|
-
|
69
|
-
def append_headers_to_log_file
|
70
|
-
file = File.new(statistic_file_path, 'a+')
|
71
|
-
file.puts trim(ObjectFile.formated_headers)
|
72
|
-
file.close
|
73
|
-
end
|
74
|
-
|
75
|
-
def check_directory_consistency!
|
76
|
-
abort directory_not_found_banner unless File.exist? @source_directory
|
77
|
-
end
|
78
|
-
|
79
|
-
def preload_log_files!
|
80
|
-
Dir.glob(File.join(@source_directory, '*.log')).each do |log_file|
|
81
|
-
@log_files << LogFile.new(log_file, @target_directory)
|
82
|
-
stdout loading_log_file_banner(log_file)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def stdout(string)
|
87
|
-
puts ' ---> ' + string
|
88
|
-
end
|
89
|
-
|
90
|
-
# ==========================================================================
|
91
|
-
# BANNERS
|
92
|
-
# ==========================================================================
|
93
|
-
|
94
|
-
def notice_banner
|
95
|
-
'Parsing logs...
|
96
|
-
This may take a long-while, go get yourself a coffee!
|
97
|
-
While I hanlde this stuff for you.
|
98
|
-
'
|
99
|
-
end
|
100
|
-
|
101
|
-
def count_of_logs_banner(size)
|
102
|
-
'Count of loaded logs : %s ' % size
|
103
|
-
end
|
104
|
-
|
105
|
-
def parsing_log_file_banner(file)
|
106
|
-
'Parsing log located at : %s ' % file
|
107
|
-
end
|
108
|
-
|
109
|
-
def loading_log_file_banner(file)
|
110
|
-
'Initializing log file: %s' % file
|
111
|
-
end
|
112
|
-
|
113
|
-
def directory_not_found_banner
|
114
|
-
'No such dir: %s' % @source_directory
|
115
|
-
end
|
116
|
-
|
117
|
-
end
|
118
|
-
|
119
|
-
end
|
data/lib/completed_line.rb
DELETED
@@ -1,109 +0,0 @@
|
|
1
|
-
module Plog
|
2
|
-
|
3
|
-
class CompletedLine
|
4
|
-
|
5
|
-
TOTAL_TIME_REGEX = /^(Completed in )(\d+)ms/ # \2
|
6
|
-
VIEW_TIME_REGEX = /([^\(]+)([^V]+)(View: )(\d+)(.*)/ # \4
|
7
|
-
DB_TIME_REGEX = /([^\(]+)([^D]+)(DB: )(\d+)(.*)/ # \4
|
8
|
-
URL_REGEX = /([^\[]+)(\[)([^\]]+)(.*)/
|
9
|
-
STATUS_REGEX = /(\s)(\d+)(\s)(\w+)(\s)(\[)(.*)/ # \2, \4
|
10
|
-
COMPLETED_TIME_REGEX = /^Completed/
|
11
|
-
|
12
|
-
def self.url
|
13
|
-
URL.new(@url)
|
14
|
-
end
|
15
|
-
|
16
|
-
def self.valid?
|
17
|
-
completed_line?
|
18
|
-
end
|
19
|
-
|
20
|
-
def self.completed_line?
|
21
|
-
return false unless @line.is_a? String
|
22
|
-
return false unless COMPLETED_TIME_REGEX =~ @line
|
23
|
-
return false unless DB_TIME_REGEX =~ @line
|
24
|
-
return false if @line.size < 10
|
25
|
-
true
|
26
|
-
end
|
27
|
-
|
28
|
-
def self.read!(string_line)
|
29
|
-
@line = string_line
|
30
|
-
validate
|
31
|
-
end
|
32
|
-
|
33
|
-
def self.db_time
|
34
|
-
@db_time
|
35
|
-
end
|
36
|
-
|
37
|
-
def self.view_time
|
38
|
-
@view_time
|
39
|
-
end
|
40
|
-
|
41
|
-
def self.total_time
|
42
|
-
@total_time
|
43
|
-
end
|
44
|
-
|
45
|
-
def self.status_string
|
46
|
-
@status_string
|
47
|
-
end
|
48
|
-
|
49
|
-
def self.status_number
|
50
|
-
@status_number
|
51
|
-
end
|
52
|
-
|
53
|
-
def self.to_csv
|
54
|
-
[total_time, view_time, db_time, url.simplify].join(',')
|
55
|
-
end
|
56
|
-
|
57
|
-
def self.merge(view, db)
|
58
|
-
@view_time += view.to_i
|
59
|
-
@db_time += db.to_i
|
60
|
-
@total_time = (@view_time + @db_time)
|
61
|
-
end
|
62
|
-
|
63
|
-
protected
|
64
|
-
|
65
|
-
def self.validate
|
66
|
-
return unless completed_line?
|
67
|
-
fragmentize!
|
68
|
-
extract_time!
|
69
|
-
extract_url!
|
70
|
-
end
|
71
|
-
|
72
|
-
def self.fragmentize!
|
73
|
-
@first_fragment, @second_fragment = @line.split('|')
|
74
|
-
end
|
75
|
-
|
76
|
-
def self.extract_time!
|
77
|
-
@total_time = extract_total_time
|
78
|
-
@view_time = extract_view_time
|
79
|
-
@db_time = extract_db_time
|
80
|
-
@status_number, @status_string = extract_status_number_and_string
|
81
|
-
nil
|
82
|
-
end
|
83
|
-
|
84
|
-
def self.extract_total_time
|
85
|
-
return 0 unless TOTAL_TIME_REGEX =~ @first_fragment
|
86
|
-
@first_fragment.gsub(TOTAL_TIME_REGEX, '\2').to_i
|
87
|
-
end
|
88
|
-
|
89
|
-
def self.extract_db_time
|
90
|
-
return 0 unless DB_TIME_REGEX =~ @first_fragment
|
91
|
-
@first_fragment.gsub(DB_TIME_REGEX, '\4').to_i
|
92
|
-
end
|
93
|
-
|
94
|
-
def self.extract_view_time
|
95
|
-
return 0 unless VIEW_TIME_REGEX =~ @first_fragment
|
96
|
-
@first_fragment.gsub(VIEW_TIME_REGEX, '\4').to_i
|
97
|
-
end
|
98
|
-
|
99
|
-
def self.extract_status_number_and_string
|
100
|
-
@second_fragment.gsub(STATUS_REGEX, '\2,\4').split(',')
|
101
|
-
end
|
102
|
-
|
103
|
-
def self.extract_url!
|
104
|
-
@url = @second_fragment.gsub(URL_REGEX, '\3')
|
105
|
-
end
|
106
|
-
|
107
|
-
end
|
108
|
-
|
109
|
-
end
|
data/lib/log_file.rb
DELETED
@@ -1,52 +0,0 @@
|
|
1
|
-
module Plog
|
2
|
-
|
3
|
-
class LogFile < File
|
4
|
-
|
5
|
-
attr_accessor :name
|
6
|
-
|
7
|
-
def initialize(file, dump_dir)
|
8
|
-
@dump_dir = dump_dir
|
9
|
-
check_dump_dir_consistency!
|
10
|
-
super(file, 'r')
|
11
|
-
@name = file
|
12
|
-
end
|
13
|
-
|
14
|
-
def check_dump_dir_consistency!
|
15
|
-
unless File.exist? objects_recipient_path
|
16
|
-
FileUtils.mkdir_p objects_recipient_path
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def validate(file)
|
21
|
-
abort 'File not found: ' + file.inspect unless File.exist? file
|
22
|
-
end
|
23
|
-
|
24
|
-
def parse_completed_lines!
|
25
|
-
each_line do |line|
|
26
|
-
CompletedLine.read! line
|
27
|
-
parse_completed_line if CompletedLine.valid?
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def parse_completed_line
|
32
|
-
of = ObjectFile.new(object_path, 'w+')
|
33
|
-
of.simplified_url = CompletedLine.url.simplify
|
34
|
-
of.append_total_time CompletedLine.total_time
|
35
|
-
of.append_db_time CompletedLine.db_time
|
36
|
-
of.append_view_time CompletedLine.view_time
|
37
|
-
of.append_hits 1
|
38
|
-
of.save_changes!
|
39
|
-
of.close
|
40
|
-
end
|
41
|
-
|
42
|
-
def object_path
|
43
|
-
File.join(objects_recipient_path, CompletedLine.url.hashify)
|
44
|
-
end
|
45
|
-
|
46
|
-
def objects_recipient_path
|
47
|
-
@objects_recipient_path ||= File.join(@dump_dir, 'objects')
|
48
|
-
end
|
49
|
-
|
50
|
-
end
|
51
|
-
|
52
|
-
end
|
data/lib/object_file.rb
DELETED
@@ -1,165 +0,0 @@
|
|
1
|
-
module Plog
|
2
|
-
|
3
|
-
class ObjectFile < File
|
4
|
-
|
5
|
-
DEFAULT_VALUES = {
|
6
|
-
:@total_hits => 0,
|
7
|
-
:@total_time => 0,
|
8
|
-
:@view_time => 0,
|
9
|
-
:@db_time => 0,
|
10
|
-
:@simplified_url => 'default'
|
11
|
-
}
|
12
|
-
|
13
|
-
KEY_ORDER = [
|
14
|
-
:@total_hits,
|
15
|
-
:@total_time,
|
16
|
-
:@view_time,
|
17
|
-
:@db_time,
|
18
|
-
:@simplified_url
|
19
|
-
]
|
20
|
-
|
21
|
-
attr_accessor :simplified_url
|
22
|
-
attr_reader :view_time, :total_time, :db_time, :total_hits
|
23
|
-
|
24
|
-
def initialize(*args)
|
25
|
-
raw_data(args.first)
|
26
|
-
super(*args)
|
27
|
-
setup!
|
28
|
-
end
|
29
|
-
|
30
|
-
def setup!
|
31
|
-
unpack_default_values if ObjectFile.zero? path
|
32
|
-
load_changes!
|
33
|
-
end
|
34
|
-
|
35
|
-
def unpack_default_values
|
36
|
-
DEFAULT_VALUES.each do |k, v|
|
37
|
-
instance_variable_set k, v
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def append_view_time(value)
|
42
|
-
@view_time += value.to_i
|
43
|
-
end
|
44
|
-
|
45
|
-
def append_db_time(value)
|
46
|
-
@db_time += value.to_i
|
47
|
-
end
|
48
|
-
|
49
|
-
def append_total_time(value)
|
50
|
-
@total_time += value
|
51
|
-
end
|
52
|
-
|
53
|
-
def append_hits(value)
|
54
|
-
@total_hits += value
|
55
|
-
end
|
56
|
-
|
57
|
-
def save_changes!
|
58
|
-
puts serialize_changes
|
59
|
-
end
|
60
|
-
|
61
|
-
def serialize_changes
|
62
|
-
KEY_ORDER.collect { |key| instance_variable_get key }.join(',')
|
63
|
-
end
|
64
|
-
|
65
|
-
def load_changes!
|
66
|
-
return if @raw_data == ''
|
67
|
-
@total_hits, @total_time, @view_time, @db_time, @simplified_url = @raw_data.split(',')
|
68
|
-
intify!
|
69
|
-
end
|
70
|
-
|
71
|
-
def intify!
|
72
|
-
@total_hits = @total_hits.to_i
|
73
|
-
@total_time = @total_time.to_i
|
74
|
-
@db_time = @db_time.to_i
|
75
|
-
@view_time = @view_time.to_i
|
76
|
-
end
|
77
|
-
|
78
|
-
def raw_data(some_string)
|
79
|
-
if File.exist? some_string
|
80
|
-
@raw_data = File.new(some_string).read
|
81
|
-
else
|
82
|
-
@raw_data = ''
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def export_settings
|
87
|
-
[
|
88
|
-
[@total_hits, [:left, 15]],
|
89
|
-
[total_time_in_secs, [:left, 15]],
|
90
|
-
[avg_total_time, [:left, 15]],
|
91
|
-
[db_time_in_secs, [:left, 15]],
|
92
|
-
[avg_db_time, [:left, 15]],
|
93
|
-
[view_time_in_secs, [:left, 15]],
|
94
|
-
[avg_view_time, [:left, 15]],
|
95
|
-
[simplified_url, [:left, 40]],
|
96
|
-
]
|
97
|
-
end
|
98
|
-
|
99
|
-
def self.export_header_settings
|
100
|
-
[
|
101
|
-
['Hits', [:left, 15]],
|
102
|
-
['Time', [:left, 15]],
|
103
|
-
['Avg-Time', [:left, 15]],
|
104
|
-
['DbTime', [:left, 15]],
|
105
|
-
['Avg-DB', [:left, 15]],
|
106
|
-
['View', [:left, 15]],
|
107
|
-
['Avg-View', [:left, 15]],
|
108
|
-
['Url', [:left, 40]],
|
109
|
-
]
|
110
|
-
end
|
111
|
-
|
112
|
-
def self.formated_headers
|
113
|
-
export_header_settings.collect do |value, setting|
|
114
|
-
if setting.first == :left
|
115
|
-
value.to_s.ljust(setting[1])
|
116
|
-
else
|
117
|
-
value.to_s.rjust(setting[1])
|
118
|
-
end
|
119
|
-
end.join('') + "\n"
|
120
|
-
end
|
121
|
-
|
122
|
-
def export_headers
|
123
|
-
export_header_settings.collect { |header| header.ljust(20) }.join('')
|
124
|
-
end
|
125
|
-
|
126
|
-
def export
|
127
|
-
export_settings.collect do |value, setting|
|
128
|
-
if setting.first == :left
|
129
|
-
value.to_s.ljust(setting[1])
|
130
|
-
else
|
131
|
-
value.to_s.rjust(setting[1])
|
132
|
-
end
|
133
|
-
end.join('')
|
134
|
-
end
|
135
|
-
|
136
|
-
def avg_total_time
|
137
|
-
return 0 if @total_time == 0
|
138
|
-
(@total_time / @total_hits / 1000.0)
|
139
|
-
end
|
140
|
-
|
141
|
-
def avg_view_time
|
142
|
-
return 0 if @view_time == 0
|
143
|
-
(@view_time / @total_hits / 1000.0)
|
144
|
-
end
|
145
|
-
|
146
|
-
def avg_db_time
|
147
|
-
return 0 if @db_time == 0
|
148
|
-
(@db_time / @total_hits / 1000.0)
|
149
|
-
end
|
150
|
-
|
151
|
-
def total_time_in_secs
|
152
|
-
@total_time / 1000.0
|
153
|
-
end
|
154
|
-
|
155
|
-
def db_time_in_secs
|
156
|
-
@db_time / 1000.0
|
157
|
-
end
|
158
|
-
|
159
|
-
def view_time_in_secs
|
160
|
-
@view_time / 1000.0
|
161
|
-
end
|
162
|
-
|
163
|
-
end
|
164
|
-
|
165
|
-
end
|
data/lib/url.rb
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
module Plog
|
2
|
-
|
3
|
-
class URL
|
4
|
-
|
5
|
-
def initialize(url)
|
6
|
-
@url = url
|
7
|
-
@obj = URI.parse URI.escape(url)
|
8
|
-
end
|
9
|
-
|
10
|
-
def to_s
|
11
|
-
@url
|
12
|
-
end
|
13
|
-
|
14
|
-
def simplify
|
15
|
-
simplify_url = @url.dup
|
16
|
-
simplifiers.each { |k, v| simplify_url.gsub!(k, v) if k =~ simplify_url }
|
17
|
-
simplify_url
|
18
|
-
end
|
19
|
-
|
20
|
-
def hashify
|
21
|
-
::MD5.hexdigest simplify
|
22
|
-
end
|
23
|
-
|
24
|
-
protected
|
25
|
-
|
26
|
-
def simplifiers
|
27
|
-
{
|
28
|
-
/\?.*/ => '?',
|
29
|
-
/\d+/ => '[0-9]',
|
30
|
-
/http:..[^\/]+/ => '',
|
31
|
-
/%.*/ => '%~'
|
32
|
-
}
|
33
|
-
end
|
34
|
-
|
35
|
-
end
|
36
|
-
|
37
|
-
end
|