logstash-cli 0.0.6 → 0.0.7

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/.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
-