plog 0.5.0 → 0.6.0
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/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
|