logstash-cli 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -33,6 +33,8 @@ Mucho inspired by a gist of the eminent @lusis - <https://gist.github.com/138807
33
33
 
34
34
  ## Commandline Options
35
35
 
36
+ ### Grep
37
+
36
38
  Usage:
37
39
  logstash-cli grep PATTERN
38
40
 
@@ -59,15 +61,54 @@ Mucho inspired by a gist of the eminent @lusis - <https://gist.github.com/138807
59
61
 
60
62
  Search logstash for a pattern
61
63
 
64
+ ### Tail
65
+
66
+ Usage:
67
+ logstash-cli tail
68
+
69
+ Options:
70
+ [--host=HOST] # Host to connect to AMQP
71
+ # Default: localhost
72
+ --amqpurl, [--url=URL] # Alternate way to specify settings via an AMQP Url f.i. amqp://logstash:foopass@localhost:5672.
73
+ This takes precendence over other settings. Note that username and password need to be percentage encoded(URL encoded) in case of special characters
74
+ [--auto-delete] # Autodelete Exchange or not
75
+ [--vhost=VHOST] # VHost to connect to AMQP
76
+ # Default: /
77
+ [--persistent] # Persistent Exchange or not
78
+ [--ssl] # Enable SSL to connect to AMQP
79
+ [--user=USER] # User to connect to AMQP
80
+ # Default: logstash
81
+ [--meta=META] # Meta Logstash fields to show
82
+ # Default: timestamp,type,message
83
+ [--format=FORMAT] # Format to use for exporting (plain,csv,json)
84
+ # Default: csv
85
+ [--key=KEY] # Routing key
86
+ # Default: #
87
+ [--port=PORT] # Port to connect to AMQP
88
+ # Default: 5672
89
+ [--exchange=EXCHANGE] # Exchange name
90
+ # Default: rawlogs
91
+ [--password=PASSWORD] # Password to connect to AMQP
92
+ # Default: foo
93
+ [--delim=DELIM] # csv delimiter
94
+ # Default: |
95
+ [--exchange-type=EXCHANGE_TYPE] # Exchange Type
96
+ # Default: direct
97
+ [--durable] # Durable Exchange or not
98
+
99
+ Stream a live feed via AMQP
100
+
101
+
62
102
  ## Examples
63
103
 
64
104
  $ logstash-cli grep --esurl="http://logger-1.jedi.be:9200" '@message:jedi4ever AND program:sshd' --last 5d --format csv --delim ':'
65
105
 
106
+ $ logstash-cli tail --amqpurl="amqp://logger-1.jedi.be:5672" --key="program.sshd"
107
+
66
108
  ## TODO
67
109
 
68
110
  - find a way to query existing instances
69
111
  - specify last 15m
70
112
  - find a way to get the results by streaming instead of loading all in memory (maybe pagination will help here)
71
- - live tailing the output
72
113
  - produce ascii histograms
73
114
  - or sparklines
@@ -1,10 +1,11 @@
1
1
  require 'rubygems'
2
2
  require 'bundler/setup'
3
3
  require 'rack'
4
+ require 'amqp'
4
5
  require 'tire'
5
6
  require 'time'
6
7
  require 'fastercsv'
7
- require 'logstash-cli/command/grep'
8
+ require 'logstash-cli/command'
8
9
 
9
10
  module LogstashCli
10
11
  class CLI < Thor
@@ -16,15 +17,39 @@ module LogstashCli
16
17
  method_option :index_prefix , :default => "logstash-", :desc => "Logstash index prefix"
17
18
  method_option :from , :default => "#{Time.now.strftime('%Y-%m-%d')}", :desc => "Begin date"
18
19
  method_option :to, :default => "#{Time.now.strftime('%Y-%m-%d')}", :desc => "End date"
19
- method_option :format , :default => 'csv', :desc => "Format to use for exporting"
20
+ method_option :format , :default => 'csv', :desc => "Format to use for exporting (plain,csv,json)"
20
21
  method_option :size , :default => 500, :desc => "Number of results to return"
21
22
  method_option :last , :default => nil, :desc => "Specify period since now f.i. 1d"
22
23
  method_option :meta , :default => "type,message", :desc => "Meta Logstash fields to show"
23
- method_option :fields , :default => "message,program", :desc => "Logstash Fields to show"
24
- method_option :delim , :default => "|", :desc => "csv delimiter"
24
+ method_option :fields , :default => "", :desc => "Logstash Fields to show"
25
+ method_option :delim , :default => "|", :desc => "plain or csv delimiter"
26
+
25
27
  def grep(pattern)
26
28
  _grep(pattern,options)
27
29
  end
28
30
 
31
+ desc "tail", "Stream a live feed via AMQP"
32
+ method_option :url, :desc => "Alternate way to specify settings via an AMQP Url f.i. amqp://logstash:foopass@localhost:5672. \n This takes precendence over other settings. Note that username and password need to be percentage encoded(URL encoded) in case of special characters",:aliases => "\--amqpurl"
33
+ method_option :user, :default => 'logstash', :desc => "User to connect to AMQP"
34
+ method_option :password, :default => 'foo', :desc => "Password to connect to AMQP"
35
+ method_option :vhost, :default => '/', :desc => "VHost to connect to AMQP"
36
+ method_option :port, :default => 5672, :desc => "Port to connect to AMQP"
37
+ method_option :host, :default => 'localhost' , :desc => "Host to connect to AMQP"
38
+ method_option :ssl, :default => false , :desc => "Enable SSL to connect to AMQP", :type => :boolean
39
+
40
+ method_option :exchange, :default => 'rawlogs', :desc => "Exchange name"
41
+ method_option :exchange_type, :default => 'direct', :desc => "Exchange Type"
42
+ method_option :durable, :default => false, :desc => "Durable Exchange or not", :type => :boolean
43
+ method_option :auto_delete, :default => false, :desc => "Autodelete Exchange or not" , :type => :boolean
44
+ method_option :persistent, :default => false, :desc => "Persistent Exchange or not", :type => :boolean
45
+ method_option :key, :default => '#', :desc => "Routing key"
46
+ method_option :format , :default => 'csv', :desc => "Format to use for exporting (plain,csv,json)"
47
+ method_option :meta, :default => "timestamp,type,message", :desc => "Meta Logstash fields to show"
48
+ method_option :delim, :default => "|", :desc => "csv delimiter"
49
+
50
+ def tail()
51
+ _tail(options)
52
+ end
53
+
29
54
  end
30
55
  end
@@ -2,7 +2,7 @@ require 'date'
2
2
 
3
3
  require 'yajl/json_gem'
4
4
 
5
- module LogstashCli::Command
5
+ module Grep
6
6
 
7
7
  def _grep(pattern,options)
8
8
  es_url = options[:esurl]
@@ -80,6 +80,7 @@ module LogstashCli::Command
80
80
  output = case options[:format]
81
81
  when 'csv' then result.to_csv({:col_sep => options[:delim]})
82
82
  when 'json' then result.to_json
83
+ when 'plain' then result.join(options[:delim])
83
84
  end
84
85
  #tstamp = Time.iso8601(res[:@timestamp]).localtime.iso8601
85
86
 
@@ -0,0 +1,67 @@
1
+ require 'yajl/json_gem'
2
+
3
+ module Tail
4
+
5
+ def _tail(options)
6
+ amqp_url = options[:url]
7
+
8
+ amqp_user = options[:user]
9
+ amqp_password = options[:password]
10
+ amqp_vhost = options[:vhost]
11
+ amqp_port = options[:port]
12
+ amqp_host = options[:host]
13
+ amqp_ssl = options[:ssl]
14
+
15
+ exchange_name = options[:exchange]
16
+ exchange_type = options[:exchange_type]
17
+ persistent = options[:persistent]
18
+ durable = options[:durable]
19
+ auto_delete = options[:autodelete]
20
+ routing_key = options[:key]
21
+ metafields = options[:meta].split(',')
22
+
23
+ begin
24
+ #connection = AMQP.connect(AMQP_OPTS.merge(:username => "amqp_gem", :password => "amqp_gem_password", :vhost => "amqp_gem_testbed"))
25
+ settings= { :host => amqp_host, :vhost => amqp_vhost, :port => amqp_port,
26
+ :user => amqp_user, :password => amqp_password ,
27
+ :ssl => amqp_ssl }
28
+
29
+ # Aqmp url can override settings
30
+ unless amqp_url.nil?
31
+ settings = aqmp_url
32
+ end
33
+
34
+ AMQP.start(settings) do |connection, open_ok|
35
+ channel = AMQP::Channel.new(connection, :auto_recovery => true)
36
+
37
+ channel.queue("", :auto_delete => auto_delete, :peristent => persistent , :durable => durable) do |queue, declare_ok|
38
+ queue.bind(exchange_name, :routing_key => routing_key)
39
+ queue.subscribe do |payload|
40
+ parsed_message = JSON.parse(payload)
41
+ result = Array.new
42
+
43
+ metafields.each do |metafield|
44
+ result << parsed_message["@#{metafield}"]
45
+ end
46
+
47
+ output = case options[:format]
48
+ when 'csv' then result.to_csv({:col_sep => options[:delim]})
49
+ when 'json' then result.to_json
50
+ when 'plain' then result.join(options[:delim])
51
+ end
52
+
53
+ puts output
54
+ result = []
55
+ end
56
+ end
57
+ end
58
+ rescue AMQP::PossibleAuthenticationFailureError => ex
59
+ puts "Possible Authentication error:\nthe AMQP connection URL used is #{amqp_url}\n\nDetail Info:\n#{ex}"
60
+ exit -1
61
+ rescue Exception => ex
62
+ puts "Error occured: #{ex}"
63
+ exit -1
64
+ end
65
+ trap("INT") { puts "Shutting down..."; connection.close { EM.stop };exit }
66
+ end
67
+ end
@@ -0,0 +1,7 @@
1
+ require 'logstash-cli/command/grep'
2
+ require 'logstash-cli/command/tail'
3
+
4
+ module LogstashCli::Command
5
+ include Grep
6
+ include Tail
7
+ end
@@ -1,3 +1,3 @@
1
1
  module LogstashCli
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
data/logstashcli.gemspec CHANGED
@@ -9,14 +9,14 @@ Gem::Specification.new do |s|
9
9
  s.email = ["patrick.debois@jedi.be"]
10
10
  s.homepage = "http://github.com/jedi4ever/logstash-cli/"
11
11
  s.summary = %q{CLI interface to logstash}
12
- s.description = %q{CLI inteface to logstash}
12
+ s.description = %q{CLI interface to logstash}
13
13
 
14
14
  s.required_rubygems_version = ">= 1.3.6"
15
15
  s.rubyforge_project = "logstash-cli"
16
16
 
17
17
  s.add_dependency "tire"
18
18
  s.add_dependency "thor"
19
- # s.add_dependency "amqp"
19
+ s.add_dependency "amqp"
20
20
  s.add_dependency "rack"
21
21
  s.add_dependency "yajl-ruby"
22
22
  s.add_dependency "fastercsv"
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-cli
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 25
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 2
10
- version: 0.0.2
9
+ - 3
10
+ version: 0.0.3
11
11
  platform: ruby
12
12
  authors:
13
13
  - Patrick Debois
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-05-24 00:00:00 Z
18
+ date: 2012-06-24 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  requirement: &id001 !ruby/object:Gem::Requirement
@@ -56,7 +56,7 @@ dependencies:
56
56
  - 0
57
57
  version: "0"
58
58
  type: :runtime
59
- name: rack
59
+ name: amqp
60
60
  version_requirements: *id003
61
61
  prerelease: false
62
62
  - !ruby/object:Gem::Dependency
@@ -70,7 +70,7 @@ dependencies:
70
70
  - 0
71
71
  version: "0"
72
72
  type: :runtime
73
- name: yajl-ruby
73
+ name: rack
74
74
  version_requirements: *id004
75
75
  prerelease: false
76
76
  - !ruby/object:Gem::Dependency
@@ -84,7 +84,7 @@ dependencies:
84
84
  - 0
85
85
  version: "0"
86
86
  type: :runtime
87
- name: fastercsv
87
+ name: yajl-ruby
88
88
  version_requirements: *id005
89
89
  prerelease: false
90
90
  - !ruby/object:Gem::Dependency
@@ -98,11 +98,25 @@ dependencies:
98
98
  - 0
99
99
  version: "0"
100
100
  type: :runtime
101
- name: json
101
+ name: fastercsv
102
102
  version_requirements: *id006
103
103
  prerelease: false
104
104
  - !ruby/object:Gem::Dependency
105
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"
114
+ type: :runtime
115
+ name: json
116
+ version_requirements: *id007
117
+ prerelease: false
118
+ - !ruby/object:Gem::Dependency
119
+ requirement: &id008 !ruby/object:Gem::Requirement
106
120
  none: false
107
121
  requirements:
108
122
  - - ">="
@@ -115,9 +129,9 @@ dependencies:
115
129
  version: 1.0.0
116
130
  type: :development
117
131
  name: bundler
118
- version_requirements: *id007
132
+ version_requirements: *id008
119
133
  prerelease: false
120
- description: CLI inteface to logstash
134
+ description: CLI interface to logstash
121
135
  email:
122
136
  - patrick.debois@jedi.be
123
137
  executables:
@@ -135,8 +149,9 @@ files:
135
149
  - bin/logstash-cli
136
150
  - lib/logstash-cli.rb
137
151
  - lib/logstash-cli/cli.rb
152
+ - lib/logstash-cli/command.rb
138
153
  - lib/logstash-cli/command/grep.rb
139
- - lib/logstash-cli/command/live.rb
154
+ - lib/logstash-cli/command/tail.rb
140
155
  - lib/logstash-cli/version.rb
141
156
  - logstashcli.gemspec
142
157
  homepage: http://github.com/jedi4ever/logstash-cli/
@@ -1,139 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # vim:filetype=ruby
3
- require 'rubygems'
4
- require 'bundler/setup'
5
- begin
6
- require 'tire'
7
- require 'slop'
8
- require 'time'
9
- rescue LoadError
10
- puts "You seem to be missing some key libraries"
11
- puts "please run `gem install bundler --no-ri --no-rdoc; bundle install`"
12
- end
13
-
14
- begin
15
- require 'yajl/json_gem'
16
- rescue LoadError
17
- puts "`gem install yajl-ruby for better performance"
18
- end
19
-
20
- opts = Slop.parse do
21
- banner "Usage: search.rb -i <index> -f <facility>"
22
- on :i, :index=, "index to search (default: logstash-#{Time.now.strftime('%Y.%m.%d')})", :default => "logstash-#{Time.now.strftime('%Y.%m.%d')}"
23
- on :f, :facility=, "REQUIRED: Facility(application name) to use", nil
24
- on :s, :size=, "number of results to return (default: 500)", true, :default => 500
25
- on :c, :class_name=, "optional class name to narrow results by", nil
26
- on :g, :grep=, "optional search on message content. Warning!!! This is slow", true
27
- on :exceptions, "toggle exception search. Warning!!! This is slow", :default => false
28
- on :live, "runs in live logging mode. This is A LOT of output. Please use non-wildcard facilities", :default => false
29
- on :fields=, Array, "optional comma-separated list of fields to display (tstamp, msg, file, class_name, service) in order (default: tstamp,service,msg)"
30
- on :h, :help, 'Print this help message', :tail => true do
31
- #puts help
32
- puts <<-EOF
33
-
34
- --------
35
- Examples
36
- --------
37
- - last 10 results log entries from curation including timestamp, classname and message:
38
-
39
- search.rb -f curation* -s 10 --fields tstamp,class_name,msg
40
-
41
- - last 5 entries for class name com.va (note the quote around * in the -f option):
42
-
43
- search.rb -f "*" -s 5 -c com.va.*
44
-
45
- - last 20 entries everywhere with timestamp, service name and message
46
-
47
- search.rb -f "*" -s 20 --fields tstamp,service,msg
48
-
49
- - last 5 exceptions everywhere
50
-
51
- search.rb -f "*" -s 5 --exceptions
52
-
53
- - live tail tracker_web
54
-
55
- search.rb -f tracker_web --live
56
-
57
- - live tail foo with custom display
58
-
59
- search.rb -f foo --live --fields tstamp,service,class_name,msg
60
-
61
- EOF
62
- exit
63
- end
64
- end
65
-
66
- if opts[:live]
67
- require 'amqp'
68
- require 'yajl/json_gem'
69
- #https://github.com/ruby-amqp/amqp/pull/74
70
- #URL naming scheme
71
- AMQP.start("amqp://logstash:bar@localhost:7777") do |connection, open_ok|
72
- channel = AMQP::Channel.new(connection, :auto_recovery => true)
73
- exchange_name = "rawlogs"
74
-
75
- channel.queue("", :auto_delete => true, :durable => false) do |queue, declare_ok|
76
- queue.bind(exchange_name, :routing_key => opts[:facility])
77
- queue.subscribe do |payload|
78
- parsed_message = JSON.parse(payload)
79
- require 'pp'
80
- pp parsed_message
81
- service = parsed_message["@fields"]["facility"]
82
- class_name = parsed_message["@fields"]["_logger"]
83
- file = parsed_message["@fields"]["file"]
84
- msg = parsed_message["@message"]
85
- #msg = parsed_message["@fields"]["full_message"]
86
- tstamp = Time.iso8601(parsed_message["@timestamp"]).localtime.iso8601
87
- fields = opts[:fields] || ["tstamp", "service", "msg"]
88
- vals = fields.map {|x| x == fields[0] ? "\e[1m[#{eval(x)}]\e[0m" : eval(x)}
89
- display = vals.join(" - ")
90
- puts display
91
- end
92
- end
93
-
94
- trap("INT") { puts "Shutting down..."; connection.close { EM.stop };exit }
95
- end
96
- end
97
-
98
- if opts[:facility].nil?
99
- puts "Facility (matches name of service) CANNOT be empty!"
100
- require 'pp'
101
- pp opts
102
- exit
103
- end
104
-
105
- begin
106
- Tire.configure {url "http://localhost:9200"}
107
- puts opts[:index]
108
- search = Tire.search(opts[:index]) do
109
- query do
110
- boolean do
111
- must { string 'HOSTNAME:shipper'}
112
- #must { string "facility:#{opts[:facility]}" } unless opts[:facility].nil?
113
- #must { string "_logger:#{opts[:class_name]}" } unless opts[:class_name].nil?
114
- #must { string "full_message:Exception*" } if opts[:exceptions]
115
- must { string "message:#{opts[:grep]}*" } if opts[:grep]
116
- end
117
- end
118
- sort do
119
- by :@timestamp, 'desc'
120
- end
121
- size opts[:size]
122
- end
123
- rescue Exception => e
124
- puts "\nSomething went wrong with the search. This is usually do to lucene query parsing of the 'grep' option"
125
- exit
126
- end
127
-
128
- search.results.sort {|a,b| a[:@timestamp] <=> b[:@timestamp] }.each do |res|
129
- service = res[:@fields][:facility]
130
- class_name = res[:@fields][:_logger]
131
- file = res[:@fields][:file]
132
- #msg = res[:@fields][:full_message]
133
- msg = res[:@fields][:message]
134
- tstamp = Time.iso8601(res[:@timestamp]).localtime.iso8601
135
- fields = opts[:fields] || ["tstamp", "service", "msg"]
136
- vals = fields.map {|x| x == fields[0] ? "\e[1m[#{eval(x)}]\e[0m" : eval(x)}
137
- display = vals.join(" - ")
138
- puts display
139
- end