lugg 0.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 269ec3ed7cc8b001cb923289fcc45a991f1d6fbb
4
+ data.tar.gz: 9f34b5919963cfc7f972887ab416d6d1e21abf92
5
+ SHA512:
6
+ metadata.gz: 4c2a1b31b30905a76ce343f57510fa5373dfbe06c499b9819d57e7e40e90cf11eb3ea6f858eeb3390f3fabe8a92d466984b39e7bf5f6b9fbe3584d35678efbef
7
+ data.tar.gz: c673f9f5112bb7233ec78c882d913bbecbb7a5f425043ae5053eae42183d0930397469b0d411ef621ea6d47082b76143ed20f7f99a6ddba40c89526889b3ed0b
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,4 @@
1
+ --color
2
+ --order rand
3
+ -I lib
4
+ --require lugg
data/.rubocop.yml ADDED
@@ -0,0 +1,36 @@
1
+ AllCops:
2
+ Includes:
3
+ - '**/*.gemspec'
4
+ - '**/Rakefile'
5
+ Excludes:
6
+ - 'bin/*'
7
+
8
+ SignalException:
9
+ EnforcedStyle: only_raise
10
+
11
+ SymbolArray:
12
+ Enabled: true
13
+
14
+ Documentation:
15
+ Exclude:
16
+ - spec/**/*.rb
17
+ - lib/lugg/version.rb
18
+
19
+ LineLength:
20
+ Exclude:
21
+ - spec/**/*.rb
22
+ - lib/lugg.rb
23
+
24
+ AndOr:
25
+ Enabled: false
26
+
27
+ ClassLength:
28
+ Exclude:
29
+ - lib/lugg/runner.rb
30
+
31
+ MethodLength:
32
+ Exclude:
33
+ - lib/lugg/runner.rb
34
+
35
+ DoubleNegation:
36
+ Enabled: false
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.1
4
+ - 2.1.0
5
+ - 2.0.0
data/.yardopts ADDED
@@ -0,0 +1,9 @@
1
+ --no-private
2
+ --title "Lugg, Query Rails log files from the command line."
3
+ --readme README.md
4
+ --markup markdown
5
+ --markup-provider kramdown
6
+ --output-dir doc/api
7
+ -
8
+ HISTORY.md
9
+ LICENSE
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in lugg.gemspec
4
+ gemspec
data/HISTORY.md ADDED
File without changes
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Arjan van der Gaag
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,89 @@
1
+ # Lugg — query Rails log files [![Build Status](https://secure.travis-ci.org/avdgaag/lugg.png?branch=master)](http://travis-ci.org/avdgaag/lugg) [![Code Climate](https://codeclimate.com/github/avdgaag/lugg.png)](https://codeclimate.com/github/avdgaag/lugg)
2
+
3
+ A tiny command line utility to search through Rails server log files and display
4
+ requests that meet certain criteria.
5
+
6
+ ## Installation
7
+
8
+ Lugg is distributed as a Ruby gem, which should be installed on most Macs and
9
+ Linux systems. Once you have ensured you have a working installation of Ruby
10
+ and Ruby gems, install the gem as follows from the command line:
11
+
12
+ $ gem install lugg
13
+
14
+ You can verify the gem has installed correctly by checking its version number:
15
+
16
+ $ lugg -v
17
+
18
+ If this generates an error, something has gone wrong. You should see something
19
+ along the lines of `lugg 1.0.0`.
20
+
21
+ ## Usage
22
+
23
+ Lugg takes one or more files as arguments or input on STDIN and redirects that
24
+ content to STDOUT -- but not before applying some filters. Content will be
25
+ parsed as Rails server log files and only the entire log entries matching your
26
+ criteria are displayed.
27
+
28
+ You supply criteria by passing in command line options. You can see a full list
29
+ of accepted options by running `lugg -h`:
30
+
31
+ ```
32
+ --and Combine previous and next clause with AND instead of OR
33
+ --get Limit to GET requests
34
+ --post Limit to POST requests
35
+ --put Limit to PUT requests
36
+ --delete Limit to DELETE requests
37
+ --head Limit to HEAD requests
38
+ --patch Limit to PATCH requests
39
+ -c, --controller CONTROLLER Limit to requests handled by CONTROLLER
40
+ -a, --action CONTROLLER_ACTION Limit to requests handled by CONTROLLER_ACTION
41
+ --json Limit to json requests
42
+ --html Limit to html requests
43
+ --xml Limit to xml requests
44
+ --csv Limit to csv requests
45
+ --pdf Limit to pdf requests
46
+ --js Limit to js requests
47
+ -f, --format FORMAT Limit to FORMAT requests
48
+ -s, --status CODE Limit requests with status code CODE
49
+ --since TIME Limit to requests made after TIME
50
+ --until TIME Limit to requests made before TIME
51
+ -d, --duration N Limit to requests longer than N ms
52
+ -u, --uri URI Limit to requests matching URI
53
+ -p, --param KEY=VAL Limit to requests with param KEY => VAL
54
+
55
+ -v, --version Display version number
56
+ -h, --help Display this message
57
+ ```
58
+
59
+ Note that all conditions are combined with OR, but you can combine two
60
+ conditions with the `--and` flag.
61
+
62
+ ### Documentation
63
+
64
+ See the inline [API docs](http://rubydoc.info/github/avdgaag/lugg/master/frames) for more information.
65
+
66
+ ## Contributing
67
+
68
+ 1. Fork it ( https://github.com/avdgaag/lugg/fork )
69
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
70
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
71
+ 4. Push to the branch (`git push origin my-new-feature`)
72
+ 5. Create new Pull Request
73
+
74
+ ### Issues
75
+
76
+ Please report any issues, defects or suggestions in the [Github issue
77
+ tracker](https://github.com/avdgaag/lugg/issues).
78
+
79
+ ### What has changed?
80
+
81
+ See the [HISTORY](https://github.com/avdgaag/lugg/blob/master/HISTORY.md) file for a detailed changelog.
82
+
83
+ ### Credits
84
+
85
+ Created by: Arjan van der Gaag
86
+ URL: [http://arjanvandergaag.nl](http://arjanvandergaag.nl)
87
+ Project homepage: [http://avdgaag.github.io/lugg](http://avdgaag.github.io/lugg)
88
+ Date: march 2014
89
+ License: [MIT-license](https://github.com/avdgaag/lugg/LICENSE) (same as Ruby)
data/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ desc 'Default: run specs.'
4
+ task default: %i[spec rubocop doc]
5
+
6
+ require 'rspec/core/rake_task'
7
+ desc 'Run specs'
8
+ RSpec::Core::RakeTask.new
9
+
10
+ require 'yard'
11
+ desc 'Generate API docs'
12
+ YARD::Rake::YardocTask.new :doc
13
+
14
+ require 'rubocop/rake_task'
15
+ desc 'Check source code against Ruby style guide'
16
+ Rubocop::RakeTask.new
17
+
18
+ desc 'Start Irb with Lugg pre-loaded'
19
+ task :console do
20
+ require 'irb'
21
+ require 'irb/completion'
22
+ require 'lugg'
23
+ ARGV.clear
24
+ IRB.start
25
+ end
data/bin/lugg ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby -w
2
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
3
+ require 'lugg'
4
+ Lugg::Runner.new(ARGV).run(ARGF)
@@ -0,0 +1,60 @@
1
+ module Lugg
2
+ # Apply a list of filters to an Enumerable object or an enumerator.
3
+ #
4
+ # This class is used to combine all the search queries into a single filter
5
+ # to process a collection with. Its input is expected to be a
6
+ # `Lugg::Streamer` enumerator, but it could be anything.
7
+ #
8
+ # By default, the collection will be passed through as-is, but you can add
9
+ # more conditions to limit the results with either callable objects (such as
10
+ # Procs) or with blocks.
11
+ #
12
+ # @example Using a proc
13
+ # filter = Filter.new
14
+ # filter.use ->(record) { record.method == 'GET' }
15
+ #
16
+ # @example Using a block
17
+ # filter = Filter.new
18
+ # filter.use { |record| record.code == 404 }
19
+ class Filter
20
+ def initialize
21
+ @conditions = []
22
+ end
23
+
24
+ # Apply all known conditions to `records`.
25
+ #
26
+ # @param [Enumerable] records
27
+ # @return [Enumerable] filtered records
28
+ def call(records)
29
+ return records unless @conditions.any?
30
+ records.select do |record|
31
+ matches?(record)
32
+ end
33
+ end
34
+
35
+ # Store a new condition to be used on the next invocation of {#call}.
36
+ #
37
+ # @param [#call] callable
38
+ # @raise ArgumentError when both a callable or block are given
39
+ # @raise ArgumentError when the given callable does not respond to #call
40
+ def use(callable = nil, &block)
41
+ unless block_given? ^ callable
42
+ raise ArgumentError, 'Supply either an argument or a block'
43
+ end
44
+
45
+ unless block_given? || callable.respond_to?(:call)
46
+ raise ArgumentError, 'Supply either a callable argument or a block'
47
+ end
48
+
49
+ @conditions << (block_given? ? block : callable)
50
+ end
51
+
52
+ private
53
+
54
+ def matches?(record)
55
+ @conditions.any? do |condition|
56
+ condition.call(record)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,81 @@
1
+ require 'time'
2
+
3
+ module Lugg
4
+ # Request is a value object representing a single log entry from start to
5
+ # finish. Its value is the original source text from the log file, but it
6
+ # defines various reader methods to extract useful information from it.
7
+ #
8
+ # Note that a request is frozen once created. Two {Request} objects with
9
+ # the same source are considered equal.
10
+ #
11
+ # @todo optimise performance
12
+ class Request
13
+ attr_reader :source, :hash
14
+
15
+ def initialize(source)
16
+ @source = source
17
+ @hash = self.class.hash ^ source.hash
18
+ freeze
19
+ end
20
+
21
+ def eql?(other)
22
+ self.class == other.class && source == other.source
23
+ end
24
+ alias_method :==, :eql?
25
+
26
+ def method
27
+ source[/^Started ([A-Z]+)/, 1]
28
+ end
29
+
30
+ def controller
31
+ source[/^Processing by (\w+)#(\w+) as (\w+)$/, 1]
32
+ end
33
+
34
+ def action
35
+ source[/^Processing by (\w+)#(\w+) as (\w+)$/, 1] + '#' +
36
+ source[/^Processing by (\w+)#(\w+) as (\w+)$/, 2]
37
+ end
38
+
39
+ def format
40
+ source[/^Processing by (\w+)#(\w+) as (\w+)$/, 3]
41
+ end
42
+
43
+ def status
44
+ source[/^Completed (\d+) (\w+)/, 2]
45
+ end
46
+
47
+ def code
48
+ source[/^Completed (\d+) (\w+)/, 1].to_i
49
+ end
50
+
51
+ def ip
52
+ source[/^Started .* for ([0-9\.]+)/, 1]
53
+ end
54
+
55
+ def timestamp
56
+ Time.parse(source[/^Started .* at (.+)$/, 1])
57
+ end
58
+
59
+ def uri
60
+ source[/^Started \w+ "([^"]+)"/, 1]
61
+ end
62
+
63
+ def path
64
+ uri.split('?').first
65
+ end
66
+
67
+ def query
68
+ uri.split('?', 2).last
69
+ end
70
+
71
+ def duration
72
+ source[/^Completed .* in (\d+)ms/, 1].to_i
73
+ end
74
+
75
+ def params
76
+ params_string = source[/^ Parameters: (.+)$/, 1]
77
+ return {} unless params_string
78
+ eval(params_string) rescue {} # rubocop:disable Eval, RescueModifier
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,32 @@
1
+ module Lugg
2
+ # RequestMatcher is a flip-flop conditional, that becomes true when compared
3
+ # to one condition, and then stays true until a new condition. It is used to
4
+ # match log entries in a log file, starting to match when encountering a line
5
+ # with `Starting...` and stopping to match when encountering a line with
6
+ # `Completed...`.
7
+ class RequestMatcher
8
+ def initialize
9
+ @active = false
10
+ @finished = false
11
+ end
12
+
13
+ def active?
14
+ !!@active
15
+ end
16
+
17
+ def finished?
18
+ !!@finished
19
+ end
20
+
21
+ def =~(line) # rubocop:disable OpMethod
22
+ if line =~ /^Started/
23
+ @active = true
24
+ elsif line =~ /^Completed/
25
+ @active = false
26
+ @finished = true
27
+ else
28
+ @active
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,150 @@
1
+ require 'lugg/version'
2
+ require 'lugg/filter'
3
+ require 'lugg/streamer'
4
+ require 'optparse'
5
+ require 'optparse/time'
6
+
7
+ module Lugg
8
+ # The runner defines the command line interface for Lugg, using the other
9
+ # components and defining command line options and their implementations.
10
+ #
11
+ # When creating a Runner object, you pass it your option flags (typically
12
+ # `ARGV`). You can then apply its conditions to an IO object (typically
13
+ # `ARGF`).
14
+ #
15
+ # @todo extract conditions into individual objects.
16
+ class Runner
17
+ attr_reader :filter
18
+ private :filter
19
+
20
+ def initialize(flags = [], filter = Filter.new)
21
+ @filter = filter
22
+ reset
23
+ options.parse!(flags)
24
+ end
25
+
26
+ def run(io)
27
+ filter.call(Streamer.new(io).records).each do |request|
28
+ puts request.source
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def reset
35
+ @combine = false
36
+ @last_block = nil
37
+ end
38
+
39
+ def combine_clauses?
40
+ @combine && @last_block
41
+ end
42
+
43
+ def add_clause(&block)
44
+ if combine_clauses?
45
+ prev_block = @last_block
46
+ filter.use { |r| prev_block.call(r) && block.call(r) }
47
+ reset
48
+ else
49
+ filter.use(&block)
50
+ @last_block = block
51
+ end
52
+ end
53
+
54
+ def options
55
+ @options ||= OptionParser.new do |o|
56
+ o.banner = <<-EOS
57
+ Usage: lugg [options] FILE
58
+
59
+ Parses log entries from FILE or STDIN and uses [options] to control what is
60
+ sent STDOUT.
61
+ EOS
62
+ o.separator ''
63
+
64
+ o.on '--and',
65
+ 'Combine previous and next clause with AND instead of OR' do
66
+ @combine = true
67
+ end
68
+
69
+ %w(get post put delete head patch).each do |verb|
70
+ o.on "--#{verb}", "Limit to #{verb.upcase} requests" do
71
+ add_clause { |r| r.method == verb.upcase }
72
+ end
73
+ end
74
+
75
+ o.on '-c',
76
+ '--controller CONTROLLER',
77
+ 'Limit to requests handled by CONTROLLER' do |controller|
78
+ add_clause { |r| r.controller == controller }
79
+ end
80
+
81
+ o.on '-a',
82
+ '--action CONTROLLER_ACTION',
83
+ 'Limit to requests handled by CONTROLLER_ACTION' do |ca|
84
+ add_clause { |r| r.action == ca }
85
+ end
86
+
87
+ %w(json html xml csv pdf js).each do |format|
88
+ o.on "--#{format}", "Limit to #{format} requests" do
89
+ add_clause { |r| r.format.downcase == format }
90
+ end
91
+ end
92
+
93
+ o.on '-f', '--format FORMAT', 'Limit to FORMAT requests' do |format|
94
+ add_clause { |r| r.format.downcase == format.downcase }
95
+ end
96
+
97
+ o.on '-s',
98
+ '--status CODE',
99
+ 'Limit requests with status code CODE' do |code|
100
+ add_clause { |r| r.code == code }
101
+ end
102
+
103
+ o.on '--since TIME',
104
+ Time,
105
+ 'Limit to requests made after TIME' do |time|
106
+ add_clause { |r| r.timestamp > time }
107
+ end
108
+
109
+ o.on '--until TIME',
110
+ Time,
111
+ 'Limit to requests made before TIME' do |time|
112
+ add_clause { |r| r.timestamp < time }
113
+ end
114
+
115
+ o.on '-d',
116
+ '--duration N',
117
+ Integer,
118
+ 'Limit to requests longer than N ms' do |n|
119
+ add_clause { |r| r.duration > n }
120
+ end
121
+
122
+ o.on '-u',
123
+ '--uri URI',
124
+ Regexp,
125
+ 'Limit to requests matching URI' do |uri|
126
+ add_clause { |r| r.uri =~ uri }
127
+ end
128
+
129
+ o.on '-p',
130
+ '--param KEY=VAL',
131
+ 'Limit to requests with param KEY => VAL' do |param|
132
+ key, value = param.split('=', 2)
133
+ add_clause { |r| r.params[key] == value }
134
+ end
135
+
136
+ o.separator ''
137
+
138
+ o.on_tail '-v', '--version', 'Display version number' do
139
+ puts Lugg::VERSION
140
+ exit
141
+ end
142
+
143
+ o.on_tail '-h', '--help', 'Display this message' do
144
+ puts o
145
+ exit
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,31 @@
1
+ require 'lugg/request'
2
+ require 'lugg/request_matcher'
3
+
4
+ module Lugg
5
+ # The Streamer reads in content from an IO object and returns an Enumerator
6
+ # yielding {Request} objects.
7
+ class Streamer
8
+ attr_reader :io
9
+ private :io
10
+
11
+ def initialize(io)
12
+ @io = io
13
+ end
14
+
15
+ # @return [Enumerator]
16
+ def records # rubocop:disable MethodLength
17
+ Enumerator.new do |yielder|
18
+ buffer = ''
19
+ matcher = RequestMatcher.new
20
+ io.each do |line|
21
+ buffer << line if matcher =~ line
22
+ if matcher.finished?
23
+ yielder << Request.new(buffer)
24
+ matcher = RequestMatcher.new
25
+ buffer = ''
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,3 @@
1
+ module Lugg
2
+ VERSION = '0.0.1'
3
+ end
data/lib/lugg.rb ADDED
@@ -0,0 +1,44 @@
1
+ require 'lugg/version'
2
+ require 'lugg/runner'
3
+
4
+ # A tiny command line utility to search through Rails server log files and
5
+ # display requests that meet certain criteria.
6
+ #
7
+ # Lugg takes one or more files as arguments or input on STDIN and redirects
8
+ # that content to STDOUT -- but not before applying some filters. Content will
9
+ # be parsed as Rails server log files and only the entire log entries matching
10
+ # your criteria are displayed.
11
+ #
12
+ # You supply criteria by passing in command line options. You can see a full
13
+ # list of accepted options by running `lugg -h`:
14
+ #
15
+ # --and Combine previous and next clause with AND instead of OR
16
+ # --get Limit to GET requests
17
+ # --post Limit to POST requests
18
+ # --put Limit to PUT requests
19
+ # --delete Limit to DELETE requests
20
+ # --head Limit to HEAD requests
21
+ # --patch Limit to PATCH requests
22
+ # -c, --controller CONTROLLER Limit to requests handled by CONTROLLER
23
+ # -a, --action CONTROLLER_ACTION Limit to requests handled by CONTROLLER_ACTION
24
+ # --json Limit to json requests
25
+ # --html Limit to html requests
26
+ # --xml Limit to xml requests
27
+ # --csv Limit to csv requests
28
+ # --pdf Limit to pdf requests
29
+ # --js Limit to js requests
30
+ # -f, --format FORMAT Limit to FORMAT requests
31
+ # -s, --status CODE Limit requests with status code CODE
32
+ # --since TIME Limit to requests made after TIME
33
+ # --until TIME Limit to requests made before TIME
34
+ # -d, --duration N Limit to requests longer than N ms
35
+ # -u, --uri URI Limit to requests matching URI
36
+ # -p, --param KEY=VAL Limit to requests with param KEY => VAL
37
+ #
38
+ # -v, --version Display version number
39
+ # -h, --help Display this message
40
+ #
41
+ # Note that all conditions are combined with OR, but you can combine two
42
+ # conditions with the `--and` flag.
43
+ module Lugg
44
+ end
data/lugg.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # encoding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'lugg/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'lugg'
8
+ spec.version = Lugg::VERSION
9
+ spec.authors = ['Arjan van der Gaag']
10
+ spec.email = ['arjan@kabisa.nl']
11
+ spec.summary = %q{Query Rails log files from the command line.}
12
+ spec.description = <<-EOS
13
+ A tiny command line utility to search through Rails server log files and
14
+ display requests that meet certain criteria.
15
+ EOS
16
+ spec.homepage = 'http://avdgaa.github.io/lugg'
17
+ spec.license = 'MIT'
18
+
19
+ spec.files = `git ls-files -z`.split("\x0")
20
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
21
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
22
+ spec.require_paths = ['lib']
23
+
24
+ spec.add_development_dependency 'bundler', '~> 1.5'
25
+ spec.add_development_dependency 'rake'
26
+ spec.add_development_dependency 'rspec'
27
+ spec.add_development_dependency 'yard'
28
+ spec.add_development_dependency 'rubocop'
29
+ end