log_aggregator 0.0.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.
- checksums.yaml +7 -0
- data/README.md +56 -0
- data/Rakefile +13 -0
- data/lib/parser.rb +5 -0
- data/lib/parser/client.rb +36 -0
- data/lib/parser/exceptions.rb +8 -0
- data/lib/parser/formats/average.rb +22 -0
- data/lib/parser/formats/base.rb +23 -0
- data/lib/parser/formats/unique_visit.rb +20 -0
- data/lib/parser/formats/visit.rb +15 -0
- data/lib/parser/presenter.rb +28 -0
- data/lib/parser/reader.rb +44 -0
- data/lib/tasks/parser.rake +20 -0
- metadata +54 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a893c3bac14478e549eaad2cdf0f4d03d1e9423a61562d4fb97a875e8912d664
|
4
|
+
data.tar.gz: 155279de2519b3faa6326c142f9abb72e5ee1e834b0e06a722c133a5c407bb91
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0e4dce7af142dd402842a23866deae9c4b9a624204cfcb8f5231a42587c4d50fdfb37b156595effc821853ca317e9ba3d874e205b6077c8d0b019a863f9365cf
|
7
|
+
data.tar.gz: 1960454b2cab71075bbd4d63eb872252e2cfa9074b5290b439bf55d5f62f205dd81763835a650990a007c69fd278706d44700e534ff8c14999ef82ee20d9b09d
|
data/README.md
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# Parser
|
2
|
+
|
3
|
+
Test app for SP: log files parser.
|
4
|
+
Parses a log file and returns information regarding total visits and unique visits count.
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
This Parser app uses ruby 2.6.
|
9
|
+
Steps to install:
|
10
|
+
* git clone https://github.com/natonnelier/ruby_app.git
|
11
|
+
* cd ruby_app && bundle install
|
12
|
+
|
13
|
+
## Usage
|
14
|
+
|
15
|
+
### List pages with most views in descending order:
|
16
|
+
```shell
|
17
|
+
rake parse:visits webserver.log #or my-file-path.log
|
18
|
+
```
|
19
|
+
|
20
|
+
Prints:
|
21
|
+
```
|
22
|
+
/about/2 90 visits
|
23
|
+
/contact 89 visits
|
24
|
+
/index 82 visits
|
25
|
+
/about 81 visits
|
26
|
+
/help_page/1 80 visits
|
27
|
+
/home 78 visits
|
28
|
+
```
|
29
|
+
|
30
|
+
### List pages with most unique page views in descending order:
|
31
|
+
```shell
|
32
|
+
rake parse:unique webserver.log #or my-file-path.log
|
33
|
+
```
|
34
|
+
|
35
|
+
Prints:
|
36
|
+
```
|
37
|
+
/help_page/1 23 unique views
|
38
|
+
/contact 23 unique views
|
39
|
+
/home 23 unique views
|
40
|
+
/index 23 unique views
|
41
|
+
/about/2 22 unique views
|
42
|
+
/about 21 unique views
|
43
|
+
```
|
44
|
+
|
45
|
+
### To run tests:
|
46
|
+
```shell
|
47
|
+
rspec spec
|
48
|
+
```
|
49
|
+
or
|
50
|
+
```shell
|
51
|
+
bundle exec rspec spec
|
52
|
+
```
|
53
|
+
or
|
54
|
+
```shell
|
55
|
+
rake spec
|
56
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
begin
|
4
|
+
Dir["lib/**/*.rb"].each { |f| require_relative(f) }
|
5
|
+
Dir["lib/tasks/*.rake"].each { |f| load(f) }
|
6
|
+
require "rspec/core/rake_task"
|
7
|
+
|
8
|
+
RSpec::Core::RakeTask.new(:spec)
|
9
|
+
|
10
|
+
task default: %w[lint spec]
|
11
|
+
rescue LoadError
|
12
|
+
# no rspec available
|
13
|
+
end
|
data/lib/parser.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "exceptions"
|
4
|
+
require_relative "reader"
|
5
|
+
|
6
|
+
module Parser
|
7
|
+
class Client
|
8
|
+
attr_reader :file, :options, :data
|
9
|
+
|
10
|
+
def self.parse(file, options = {})
|
11
|
+
new(file, options).parse
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(file, options = {})
|
15
|
+
@file = file
|
16
|
+
@options = options
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse
|
20
|
+
read_file
|
21
|
+
print_data
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def read_file
|
27
|
+
@data ||= ::Parser::Reader.new(file).call
|
28
|
+
rescue InvalidExtensionException, FileNotFoundException => e
|
29
|
+
abort e.message
|
30
|
+
end
|
31
|
+
|
32
|
+
def print_data
|
33
|
+
::Parser::Presenter.new(data, options).call
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require_relative 'base'
|
3
|
+
|
4
|
+
module Formats
|
5
|
+
class Average < Base
|
6
|
+
def call
|
7
|
+
output_data
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def output_data
|
13
|
+
sorted_data.each do |path, ips|
|
14
|
+
puts "#{path} average #{average(ips)}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def average(ips)
|
19
|
+
ips.size.to_f / ips.uniq.size
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Formats
|
4
|
+
class Base
|
5
|
+
attr_reader :data
|
6
|
+
|
7
|
+
def initialize(data)
|
8
|
+
@data = data
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def sorted_data
|
14
|
+
data.sort_by { |_, ips| -ips.size }
|
15
|
+
end
|
16
|
+
|
17
|
+
def output_data
|
18
|
+
sorted_data.each do |path, ips|
|
19
|
+
puts "#{path} #{ips.size} #{description}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Formats
|
4
|
+
class UniqueVisit < Base
|
5
|
+
def call
|
6
|
+
ips_uniq!
|
7
|
+
output_data
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def ips_uniq!
|
13
|
+
data.each { |_, ips| ips.uniq! }
|
14
|
+
end
|
15
|
+
|
16
|
+
def description
|
17
|
+
"unique views"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Parser
|
4
|
+
class Presenter
|
5
|
+
attr_accessor :data, :options
|
6
|
+
|
7
|
+
def initialize(data, options = {})
|
8
|
+
@data = data
|
9
|
+
@options = options
|
10
|
+
end
|
11
|
+
|
12
|
+
def call
|
13
|
+
output_formatted_data
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def format_class
|
19
|
+
return Formats::Average if options[:average]
|
20
|
+
return Formats::UniqueVisit if options[:unique]
|
21
|
+
Formats::Visit
|
22
|
+
end
|
23
|
+
|
24
|
+
def output_formatted_data
|
25
|
+
format_class.new(data).call
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Parser
|
4
|
+
class Reader
|
5
|
+
VALID_EXTENSIONS = %w[.log .txt].freeze
|
6
|
+
|
7
|
+
attr_accessor :file
|
8
|
+
|
9
|
+
def initialize(file)
|
10
|
+
@file = file
|
11
|
+
end
|
12
|
+
|
13
|
+
def call
|
14
|
+
validate_presence
|
15
|
+
validate_extension
|
16
|
+
aggregate
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def validate_extension
|
22
|
+
unless VALID_EXTENSIONS.include? File.extname(file)
|
23
|
+
raise InvalidExtensionException, "File has to be .log or .txt"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def validate_presence
|
28
|
+
raise FileNotFoundException, "File not found" unless file && File.exist?(file)
|
29
|
+
end
|
30
|
+
|
31
|
+
def file_lines
|
32
|
+
@file_lines ||= File.readlines file
|
33
|
+
end
|
34
|
+
|
35
|
+
def aggregate
|
36
|
+
data = Hash.new { |h, key| h[key] = [] }
|
37
|
+
file_lines.each do |line|
|
38
|
+
path, ip = line.split
|
39
|
+
data[path] << ip
|
40
|
+
end
|
41
|
+
data
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
namespace :parse do |args|
|
4
|
+
options = {}
|
5
|
+
file = ARGV[1]
|
6
|
+
|
7
|
+
task :visits do
|
8
|
+
Parser::Client.parse(file)
|
9
|
+
end
|
10
|
+
|
11
|
+
task :unique do
|
12
|
+
options[:unique] = true
|
13
|
+
Parser::Client.parse(file, options)
|
14
|
+
end
|
15
|
+
|
16
|
+
task :average do
|
17
|
+
options[:average] = true
|
18
|
+
Parser::Client.parse(file, options)
|
19
|
+
end
|
20
|
+
end
|
metadata
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: log_aggregator
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Nicolas Tonnelier
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-02-05 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Parse and count requests by endpoints
|
14
|
+
email: ''
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- README.md
|
20
|
+
- Rakefile
|
21
|
+
- lib/parser.rb
|
22
|
+
- lib/parser/client.rb
|
23
|
+
- lib/parser/exceptions.rb
|
24
|
+
- lib/parser/formats/average.rb
|
25
|
+
- lib/parser/formats/base.rb
|
26
|
+
- lib/parser/formats/unique_visit.rb
|
27
|
+
- lib/parser/formats/visit.rb
|
28
|
+
- lib/parser/presenter.rb
|
29
|
+
- lib/parser/reader.rb
|
30
|
+
- lib/tasks/parser.rake
|
31
|
+
homepage: https://rubygems.org/gems/log_aggregator
|
32
|
+
licenses:
|
33
|
+
- MIT
|
34
|
+
metadata: {}
|
35
|
+
post_install_message:
|
36
|
+
rdoc_options: []
|
37
|
+
require_paths:
|
38
|
+
- lib
|
39
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
requirements: []
|
50
|
+
rubygems_version: 3.0.3
|
51
|
+
signing_key:
|
52
|
+
specification_version: 4
|
53
|
+
summary: Log Parser and Aggregator
|
54
|
+
test_files: []
|