logstash-cli 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
data/.rvmrc CHANGED
@@ -1,5 +1,5 @@
1
1
  #this uses rvm
2
- rvm use 1.8.7
2
+ rvm use 1.9.2
3
3
  rvm_gemset_create_on_use_flag=1
4
4
  rvm gemset use logstash-cli
5
5
  alias rake="bundle exec rake"
data/README.md CHANGED
@@ -25,6 +25,10 @@ Mucho inspired by a gist of the eminent @lusis - <https://gist.github.com/138807
25
25
 
26
26
  ### Using the Gem
27
27
 
28
+ # If you no rvm
29
+ $ bundle exec bin/logstash-cli
30
+
31
+ # If you have rvm , there is an alias in .rvmrc
28
32
  $ logstash-cli
29
33
 
30
34
  ### Using the Github version - through bundler
@@ -46,18 +50,18 @@ Mucho inspired by a gist of the eminent @lusis - <https://gist.github.com/138807
46
50
  [--meta=META] # Meta Logstash fields to show
47
51
  # Default: type,message
48
52
  [--to=TO] # End date
49
- # Default: Today in YYYY-MM-DD form
53
+ # Default: Today in YYYY-MM-DD HH:MM:SS form (the time is optional)
50
54
  [--delim=DELIM] # plain or csv delimiter
51
55
  # Default: |
52
56
  [--format=FORMAT] # Format to use for exporting
53
57
  # Default: csv
54
58
  [--from=FROM] # Begin date
55
- # Default: Today in YYYY-MM-DD form
59
+ # Default: Today in YYYY-MM-DD HH:MM:SS form (the time is optional)
56
60
  [--size=SIZE] # Number of results to return
57
61
  # Default: 500
58
62
  [--esurl=ESURL] # URL to connect to elasticsearch
59
63
  # Default: http://localhost:9200
60
- [--last=LAST] # Specify period since now f.i. 1d
64
+ [--last=LAST] # Specify period since now (Examples: 10min, 3hrs, 4days, 1wk, 1yr)
61
65
 
62
66
  Search logstash for a pattern
63
67
 
@@ -138,7 +142,6 @@ Mucho inspired by a gist of the eminent @lusis - <https://gist.github.com/138807
138
142
  ## TODO
139
143
 
140
144
  - find a way to query existing instances
141
- - specify last 15m
142
145
  - find a way to get the results by streaming instead of loading all in memory (maybe pagination will help here)
143
146
  - produce ascii histograms
144
147
  - or sparklines
@@ -18,11 +18,11 @@ module LogstashCli
18
18
  desc "grep PATTERN", "Search logstash for a pattern"
19
19
  method_option :esurl , :default => 'http://localhost:9200', :desc => "URL to connect to elasticsearch"
20
20
  method_option :index_prefix , :default => "logstash-", :desc => "Logstash index prefix"
21
- method_option :from , :default => "#{Time.now.strftime('%Y-%m-%d')}", :desc => "Begin date"
22
- method_option :to, :default => "#{Time.now.strftime('%Y-%m-%d')}", :desc => "End date"
21
+ method_option :from , :default => nil, :desc => "Begin date and (optionally) time."
22
+ method_option :to, :default => nil, :desc => "End date and (optionally) time"
23
23
  method_option :format , :default => 'plain', :desc => "Format to use for exporting (plain,csv,json)"
24
24
  method_option :size , :default => 500, :desc => "Number of results to return"
25
- method_option :last , :default => nil, :desc => "Specify period since now f.i. 1d"
25
+ method_option :last , :default => '1d', :desc => "Specify period since now f.i. 1d"
26
26
  method_option :meta , :default => "type,message", :desc => "Meta Logstash fields to show"
27
27
  method_option :fields , :default => "", :desc => "Logstash Fields to show"
28
28
  method_option :delim , :default => "|", :desc => "plain or csv delimiter"
@@ -65,7 +65,8 @@ module Count
65
65
  puts _format(header, options)
66
66
 
67
67
  results['terms'].each do |terms|
68
- puts _format(terms.values, options)
68
+ result = [ terms['term'], terms['count'] ]
69
+ puts _format(result, options)
69
70
 
70
71
  unless fields.empty? and metafields.empty?
71
72
  term = terms['term']
@@ -4,32 +4,60 @@ require 'yajl/json_gem'
4
4
 
5
5
  module Grep
6
6
 
7
+ def self.indexes_from_interval(from, to)
8
+ (from.to_date..to.to_date).sort.map do |date|
9
+ date.to_s.gsub('-', '.')
10
+ end
11
+ end
12
+
13
+ # Very naive time range description parsing.
14
+ def self.parse_time_range(desc)
15
+ /(?<value>\d+)\s*(?<units>\w*)/ =~ desc
16
+ value = value.to_i
17
+ start = case units.to_s.downcase
18
+ when 'm', 'min', 'mins', 'minute', 'minutes'
19
+ DateTime.now - (value/(60*24.0))
20
+ when 'h', 'hr', 'hrs', 'hour', 'hours'
21
+ DateTime.now - (value/24.0)
22
+ when 'd', 'day', 'days'
23
+ DateTime.now - value
24
+ when 'w', 'wk', 'wks', 'week', 'weeks'
25
+ DateTime.now - (7.0*value)
26
+ when 'y', 'yr', 'yrs', 'year', 'years'
27
+ DateTime.now - (365.0*value)
28
+ else
29
+ raise ArgumentError
30
+ end
31
+ [start, DateTime.now]
32
+ end
33
+
7
34
  def _grep(pattern,options)
8
35
  es_url = options[:esurl]
9
36
  index_prefix = options[:index_prefix]
10
-
11
- from = options[:from]
12
- to = options[:to]
13
37
  metafields = options[:meta].split(',')
14
38
  fields = options[:fields].split(',')
15
39
 
16
40
  begin
17
- unless options[:last].nil?
18
- days = options[:last].match(/(\d*)d/)[1].to_i
19
- to_date = Date.today
20
- from_date = to_date - days
21
- from = from_date.to_s
22
- to = to_date.to_s
41
+ if options[:last].nil?
42
+ from_time = DateTime.parse(options[:from])
43
+ to_time = DateTime.parse(options[:to])
44
+ else
45
+ from_time, to_time = Grep.parse_time_range(options[:last])
23
46
  end
24
-
25
- from_date = Date.parse(from)
26
- to_date = Date.parse(to)
27
- rescue Exception => ex
28
- $stderr.puts "Something went wrong while parsing the dates: currently only dates are supported with last. Be sure to add the suffix 'd' "+ex
47
+ rescue ArgumentError
48
+ $stderr.puts "Something went wrong while parsing the date range."
29
49
  exit -1
30
50
  end
31
51
 
32
- $stderr.puts "Searching #{es_url}[#{index_prefix}#{from_date}..#{index_prefix}#{to_date}] - #{pattern}"
52
+ index_range = Grep.indexes_from_interval(from_time, to_time).map do |i|
53
+ "#{index_prefix}#{i}"
54
+ end
55
+
56
+ $stderr.puts "Searching #{es_url}[#{index_range.first}..#{index_range.last}] - #{pattern}"
57
+
58
+ # Reformat time interval to match logstash's internal timestamp'
59
+ from = from_time.to_time.utc.strftime('%FT%T')
60
+ to = to_time.to_time.utc.strftime('%FT%T')
33
61
 
34
62
  # Total of results to show
35
63
  total_result_size = options[:size]
@@ -40,19 +68,17 @@ module Grep
40
68
  running_result_size = total_result_size.to_i
41
69
 
42
70
  # We reverse the order of working ourselves through the index
43
- (from_date..to_date).sort.reverse.to_a.each do |date|
44
-
45
- es_index = index_prefix+date.to_s.gsub('-','.')
46
-
71
+ index_range.reverse.each do |idx|
47
72
  begin
48
73
  Tire.configure {url es_url}
49
- search = Tire.search(es_index) do
74
+ search = Tire.search(idx) do
50
75
  query do
51
76
  string "#{pattern}"
52
77
  end
53
78
  sort do
54
79
  by :@timestamp, 'desc'
55
80
  end
81
+ filter "range", "@timestamp" => { "from" => from, "to" => to}
56
82
  size running_result_size
57
83
  end
58
84
  rescue Exception => e
@@ -81,7 +107,8 @@ module Grep
81
107
  result = []
82
108
  end
83
109
  rescue ::Tire::Search::SearchRequestFailed => e
84
- $stderr.puts e.message
110
+ # If we got a 404 it likely means we simply don't have logs for that day, not failing over necessarily.
111
+ $stderr.puts e.message unless search.response.code == 404
85
112
  end
86
113
  end
87
114
  end
@@ -36,7 +36,7 @@ module Tail
36
36
 
37
37
  channel = AMQP::Channel.new(connection, :auto_recovery => true)
38
38
 
39
- channel.queue("", :auto_delete => auto_delete, :peristent => persistent , :durable => durable) do |queue, declare_ok|
39
+ channel.queue("", :auto_delete => auto_delete, :persistent => persistent , :durable => durable) do |queue, declare_ok|
40
40
  queue.bind(exchange_name, :routing_key => routing_key)
41
41
  queue.subscribe do |payload|
42
42
  parsed_message = JSON.parse(payload)
@@ -1,3 +1,3 @@
1
1
  module LogstashCli
2
- VERSION = "0.0.6"
2
+ VERSION = "0.0.7"
3
3
  end
@@ -0,0 +1,58 @@
1
+ require_relative '../../spec_helper.rb'
2
+ require_relative '../../../lib/logstash-cli/command/grep.rb'
3
+
4
+ describe Grep do
5
+ it "determines index range needed for a given date range" do
6
+ Grep.indexes_from_interval(DateTime.now, DateTime.now).should == [DateTime.now.to_date.to_s.gsub('-', '.')]
7
+ end
8
+
9
+ it "calculates time range required to search last X minutes" do
10
+ # The to_i removes fractions seconds. Unsure on the best way to test this.
11
+ diff = 10*(1/60.0*24.0)
12
+ Grep.parse_time_range("10m").map{|t| t.to_time.to_i}.should == [DateTime.now - diff, DateTime.now].map{|t| t.to_time.to_i}
13
+ Grep.parse_time_range("10mins").map{|t| t.to_time.to_i}.should == [DateTime.now - diff, DateTime.now].map{|t| t.to_time.to_i}
14
+ Grep.parse_time_range("10 min").map{|t| t.to_time.to_i}.should == [DateTime.now - diff, DateTime.now].map{|t| t.to_time.to_i}
15
+ Grep.parse_time_range("10 minute").map{|t| t.to_time.to_i}.should == [DateTime.now - diff, DateTime.now].map{|t| t.to_time.to_i}
16
+ Grep.parse_time_range("10 minutes").map{|t| t.to_time.to_i}.should == [DateTime.now - diff, DateTime.now].map{|t| t.to_time.to_i}
17
+ end
18
+
19
+ it "calculates time range required to search last X hours" do
20
+ diff = 10*(1/24.0)
21
+ Grep.parse_time_range("10h").map{|t| t.to_time.to_i}.should == [DateTime.now - diff, DateTime.now].map{|t| t.to_time.to_i}
22
+ Grep.parse_time_range("10hr").map{|t| t.to_time.to_i}.should == [DateTime.now - diff, DateTime.now].map{|t| t.to_time.to_i}
23
+ Grep.parse_time_range("10hrs").map{|t| t.to_time.to_i}.should == [DateTime.now - diff, DateTime.now].map{|t| t.to_time.to_i}
24
+ Grep.parse_time_range("10 hour").map{|t| t.to_time.to_i}.should == [DateTime.now - diff, DateTime.now].map{|t| t.to_time.to_i}
25
+ Grep.parse_time_range("10 hours").map{|t| t.to_time.to_i}.should == [DateTime.now - diff, DateTime.now].map{|t| t.to_time.to_i}
26
+ end
27
+
28
+ it "calculates time range required to search last X days" do
29
+ diff = 10
30
+ Grep.parse_time_range("10d").map{|t| t.to_time.to_i}.should == [DateTime.now - diff, DateTime.now].map{|t| t.to_time.to_i}
31
+ Grep.parse_time_range("10days").map{|t| t.to_time.to_i}.should == [DateTime.now - diff, DateTime.now].map{|t| t.to_time.to_i}
32
+ Grep.parse_time_range("10 day").map{|t| t.to_time.to_i}.should == [DateTime.now - diff, DateTime.now].map{|t| t.to_time.to_i}
33
+ end
34
+
35
+ it "calculates time range required to search last X weeks" do
36
+ diff = 10*7
37
+ Grep.parse_time_range("10w").map{|t| t.to_time.to_i}.should == [DateTime.now - diff, DateTime.now].map{|t| t.to_time.to_i}
38
+ Grep.parse_time_range("10wk").map{|t| t.to_time.to_i}.should == [DateTime.now - diff, DateTime.now].map{|t| t.to_time.to_i}
39
+ Grep.parse_time_range("10 wks").map{|t| t.to_time.to_i}.should == [DateTime.now - diff, DateTime.now].map{|t| t.to_time.to_i}
40
+ Grep.parse_time_range("10 week").map{|t| t.to_time.to_i}.should == [DateTime.now - diff, DateTime.now].map{|t| t.to_time.to_i}
41
+ Grep.parse_time_range("10 weeks").map{|t| t.to_time.to_i}.should == [DateTime.now - diff, DateTime.now].map{|t| t.to_time.to_i}
42
+ end
43
+
44
+ it "calculates time range required to search last X years" do
45
+ diff = 10*365
46
+ Grep.parse_time_range("10y").map{|t| t.to_time.to_i}.should == [DateTime.now - diff, DateTime.now].map{|t| t.to_time.to_i}
47
+ Grep.parse_time_range("10 yr").map{|t| t.to_time.to_i}.should == [DateTime.now - diff, DateTime.now].map{|t| t.to_time.to_i}
48
+ Grep.parse_time_range("10 yrs").map{|t| t.to_time.to_i}.should == [DateTime.now - diff, DateTime.now].map{|t| t.to_time.to_i}
49
+ Grep.parse_time_range("10 year").map{|t| t.to_time.to_i}.should == [DateTime.now - diff, DateTime.now].map{|t| t.to_time.to_i}
50
+ Grep.parse_time_range("10 years").map{|t| t.to_time.to_i}.should == [DateTime.now - diff, DateTime.now].map{|t| t.to_time.to_i}
51
+ end
52
+
53
+ it "raises an ArgumentError if it can't cacluate the time range" do
54
+ lambda {Grep.parse_time_range("two-weeks")}.should raise_error ArgumentError
55
+ lambda {Grep.parse_time_range("10parsecs")}.should raise_error ArgumentError
56
+ end
57
+
58
+ end
File without changes
metadata CHANGED
@@ -1,146 +1,152 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: logstash-cli
3
- version: !ruby/object:Gem::Version
4
- hash: 19
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.7
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 0
9
- - 6
10
- version: 0.0.6
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Patrick Debois
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2012-08-21 00:00:00 Z
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
21
- requirement: &id001 !ruby/object:Gem::Requirement
22
- none: false
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- hash: 3
27
- segments:
28
- - 0
29
- version: "0"
30
- type: :runtime
12
+ date: 2012-12-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
31
15
  name: tire
32
- version_requirements: *id001
33
- prerelease: false
34
- - !ruby/object:Gem::Dependency
35
- requirement: &id002 !ruby/object:Gem::Requirement
36
- none: false
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- hash: 3
41
- segments:
42
- - 0
43
- version: "0"
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
44
22
  type: :runtime
45
- name: thor
46
- version_requirements: *id002
47
23
  prerelease: false
48
- - !ruby/object:Gem::Dependency
49
- requirement: &id003 !ruby/object:Gem::Requirement
50
- none: false
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- hash: 3
55
- segments:
56
- - 0
57
- version: "0"
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: thor
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
58
38
  type: :runtime
59
- name: amqp
60
- version_requirements: *id003
61
39
  prerelease: false
62
- - !ruby/object:Gem::Dependency
63
- requirement: &id004 !ruby/object:Gem::Requirement
64
- none: false
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- hash: 3
69
- segments:
70
- - 0
71
- version: "0"
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: amqp
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
72
54
  type: :runtime
73
- name: rack
74
- version_requirements: *id004
75
55
  prerelease: false
76
- - !ruby/object:Gem::Dependency
77
- requirement: &id005 !ruby/object:Gem::Requirement
78
- none: false
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- hash: 3
83
- segments:
84
- - 0
85
- version: "0"
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rack
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
86
70
  type: :runtime
87
- name: yajl-ruby
88
- version_requirements: *id005
89
71
  prerelease: false
90
- - !ruby/object:Gem::Dependency
91
- requirement: &id006 !ruby/object:Gem::Requirement
92
- none: false
93
- requirements:
94
- - - ">="
95
- - !ruby/object:Gem::Version
96
- hash: 3
97
- segments:
98
- - 0
99
- version: "0"
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: yajl-ruby
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
100
86
  type: :runtime
101
- name: fastercsv
102
- version_requirements: *id006
103
87
  prerelease: false
104
- - !ruby/object:Gem::Dependency
105
- requirement: &id007 !ruby/object:Gem::Requirement
106
- none: false
107
- requirements:
108
- - - ">="
109
- - !ruby/object:Gem::Version
110
- hash: 3
111
- segments:
112
- - 0
113
- version: "0"
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: fastercsv
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
114
102
  type: :runtime
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
115
111
  name: json
116
- version_requirements: *id007
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
117
119
  prerelease: false
118
- - !ruby/object:Gem::Dependency
119
- requirement: &id008 !ruby/object:Gem::Requirement
120
- none: false
121
- requirements:
122
- - - ">="
123
- - !ruby/object:Gem::Version
124
- hash: 23
125
- segments:
126
- - 1
127
- - 0
128
- - 0
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: bundler
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
129
133
  version: 1.0.0
130
134
  type: :development
131
- name: bundler
132
- version_requirements: *id008
133
135
  prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: 1.0.0
134
142
  description: CLI interface to logstash
135
- email:
143
+ email:
136
144
  - patrick.debois@jedi.be
137
- executables:
145
+ executables:
138
146
  - logstash-cli
139
147
  extensions: []
140
-
141
148
  extra_rdoc_files: []
142
-
143
- files:
149
+ files:
144
150
  - .gitignore
145
151
  - .rvmrc
146
152
  - Gemfile
@@ -155,40 +161,33 @@ files:
155
161
  - lib/logstash-cli/command/tail.rb
156
162
  - lib/logstash-cli/version.rb
157
163
  - logstashcli.gemspec
164
+ - spec/logstash-cli/command/grep_spec.rb
165
+ - spec/spec_helper.rb
158
166
  homepage: http://github.com/jedi4ever/logstash-cli/
159
167
  licenses: []
160
-
161
168
  post_install_message:
162
169
  rdoc_options: []
163
-
164
- require_paths:
170
+ require_paths:
165
171
  - lib
166
- required_ruby_version: !ruby/object:Gem::Requirement
172
+ required_ruby_version: !ruby/object:Gem::Requirement
167
173
  none: false
168
- requirements:
169
- - - ">="
170
- - !ruby/object:Gem::Version
171
- hash: 3
172
- segments:
174
+ requirements:
175
+ - - ! '>='
176
+ - !ruby/object:Gem::Version
177
+ version: '0'
178
+ segments:
173
179
  - 0
174
- version: "0"
175
- required_rubygems_version: !ruby/object:Gem::Requirement
180
+ hash: 3090991630995914937
181
+ required_rubygems_version: !ruby/object:Gem::Requirement
176
182
  none: false
177
- requirements:
178
- - - ">="
179
- - !ruby/object:Gem::Version
180
- hash: 23
181
- segments:
182
- - 1
183
- - 3
184
- - 6
183
+ requirements:
184
+ - - ! '>='
185
+ - !ruby/object:Gem::Version
185
186
  version: 1.3.6
186
187
  requirements: []
187
-
188
188
  rubyforge_project: logstash-cli
189
- rubygems_version: 1.8.12
189
+ rubygems_version: 1.8.24
190
190
  signing_key:
191
191
  specification_version: 3
192
192
  summary: CLI interface to logstash
193
193
  test_files: []
194
-