logstash-cli 0.0.2 → 0.0.3

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