logstash-cli 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ Gemfile.lock
data/.rvmrc ADDED
@@ -0,0 +1,6 @@
1
+ #this uses rvm
2
+ rvm use 1.8.7
3
+ rvm_gemset_create_on_use_flag=1
4
+ rvm gemset use logstash-cli
5
+ alias rake="bundle exec rake"
6
+ alias logstash-cli="bundle exec bin/logstash-cli"
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "logstash-cli", :path => "."
4
+
5
+ group :test do
6
+ gem "rake"
7
+ end
8
+
9
+ gemspec
data/README.md ADDED
@@ -0,0 +1,55 @@
1
+ ** Work in progress **
2
+
3
+ ## Description
4
+
5
+ A cli tool to query an elasticsearch host for logstash information.
6
+ Because let's face it, we're CLI junkies :)
7
+
8
+ Mucho inspired by a gist of the eminent @lusis - <https://gist.github.com/1388077>
9
+
10
+ ## Installation
11
+ Installation using the usual steps
12
+
13
+ Install rvm , (no gem yet)
14
+
15
+ $ git clone thisrepo
16
+ $ cd thisrepo
17
+ $ gem install bundler
18
+ $ bundle install
19
+
20
+ ## Usage
21
+
22
+ Usage:
23
+ logstash-cli grep PATTERN
24
+
25
+ Options:
26
+ [--index-prefix=INDEX_PREFIX] # Logstash index prefix
27
+ # Default: logstash-
28
+ [--fields=FIELDS] # Logstash Fields to show
29
+ # Default: message,program
30
+ [--meta=META] # Meta Logstash fields to show
31
+ # Default: type,message
32
+ [--to=TO] # End date
33
+ # Default: 2012-05-11
34
+ [--delim=DELIM] # csv delimiter
35
+ # Default: |
36
+ [--format=FORMAT] # Format to use for exporting
37
+ # Default: csv
38
+ [--from=FROM] # Begin date
39
+ # Default: 2012-05-11
40
+ [--size=SIZE] # Number of results to return
41
+ # Default: 500
42
+ [--esurl=ESURL] # URL to connect to elasticsearch
43
+ # Default: http://localhost:9200
44
+ [--last=LAST] # Specify period since now f.i. 1d
45
+
46
+ Search logstash for a pattern
47
+
48
+ ## Examples
49
+
50
+ $ logstash-cli grep --esurl="http://logger-1.jedi.be:9200" '@message:jedi4ever AND program:sshd' --last 5d --format csv --delim ':'
51
+ ## TODO
52
+
53
+ - find a way to query existing instances
54
+ - specify last 15m
55
+ - find a way to get the results by streaming instead of loading all in memory (maybe pagination will help here)
data/bin/logstash-cli ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ require 'logstash-cli'
3
+
4
+ # Disable color if the proper argument was passed
5
+ shell = ARGV.include?("--no-color") ? Thor::Shell::Basic.new : Thor::Base.shell.new
6
+
7
+ # Start the CLI
8
+ ::LogstashCli::CLI.start(ARGV)
@@ -0,0 +1,30 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'rack'
4
+ require 'tire'
5
+ require 'time'
6
+ require 'fastercsv'
7
+ require 'logstash-cli/command/grep'
8
+
9
+ module LogstashCli
10
+ class CLI < Thor
11
+
12
+ include LogstashCli::Command
13
+
14
+ desc "grep PATTERN", "Search logstash for a pattern"
15
+ method_option :esurl , :default => 'http://localhost:9200', :desc => "URL to connect to elasticsearch"
16
+ method_option :index_prefix , :default => "logstash-", :desc => "Logstash index prefix"
17
+ method_option :from , :default => "#{Time.now.strftime('%Y-%m-%d')}", :desc => "Begin date"
18
+ 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 :size , :default => 500, :desc => "Number of results to return"
21
+ method_option :last , :default => nil, :desc => "Specify period since now f.i. 1d"
22
+ 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"
25
+ def grep(pattern)
26
+ _grep(pattern,options)
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,82 @@
1
+ require 'date'
2
+
3
+ require 'yajl/json_gem'
4
+
5
+ module LogstashCli::Command
6
+
7
+ def _grep(pattern,options)
8
+ es_url = options[:esurl]
9
+ index_prefix = options[:index_prefix]
10
+
11
+ from = options[:from]
12
+ to = options[:to]
13
+ metafields = options[:meta].split(',')
14
+ fields = options[:fields].split(',')
15
+
16
+ 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
23
+ 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
29
+ exit -1
30
+ end
31
+
32
+ $stderr.puts "Searching #{es_url}[#{index_prefix}#{from_date}..#{index_prefix}#{to_date}] - #{pattern}"
33
+
34
+ (from_date..to_date).to_a.each do |date|
35
+ es_index = index_prefix+date.to_s.gsub('-','.')
36
+
37
+ result_size = options[:size]
38
+
39
+ begin
40
+ Tire.configure {url es_url}
41
+ search = Tire.search(es_index) do
42
+ query do
43
+ string "#{pattern}"
44
+ end
45
+ sort do
46
+ by :@timestamp, 'desc'
47
+ end
48
+ size result_size
49
+ end
50
+ rescue Exception => e
51
+ $stderr.puts e
52
+ $stderr.puts "\nSomething went wrong with the search. This is usually due to lucene query parsing of the 'grep' option"
53
+ exit
54
+ end
55
+
56
+ begin
57
+ result = Array.new
58
+ search.results.sort {|a,b| a[:@timestamp] <=> b[:@timestamp] }.each do |res|
59
+
60
+ metafields.each do |metafield|
61
+ result << res["@#{metafield}".to_sym]
62
+ end
63
+
64
+ fields.each do |field|
65
+ result << res[:@fields][field.to_sym]
66
+ end
67
+
68
+ output = case options[:format]
69
+ when 'csv' then result.to_csv({:col_sep => options[:delim]})
70
+ when 'json' then result.to_json
71
+ end
72
+ #tstamp = Time.iso8601(res[:@timestamp]).localtime.iso8601
73
+
74
+ puts output
75
+ result = []
76
+ end
77
+ rescue ::Tire::Search::SearchRequestFailed => e
78
+ $stderr.puts e.message
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,139 @@
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
@@ -0,0 +1,3 @@
1
+ module LogstashCli
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,3 @@
1
+ require 'thor'
2
+
3
+ require 'logstash-cli/cli'
@@ -0,0 +1,32 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path("../lib/logstash-cli/version", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "logstash-cli"
6
+ s.version = LogstashCli::VERSION
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = ["Patrick Debois"]
9
+ s.email = ["patrick.debois@jedi.be"]
10
+ s.homepage = "http://github.com/jedi4ever/logstash-cli/"
11
+ s.summary = %q{CLI interface to logstash}
12
+ s.description = %q{CLI inteface to logstash}
13
+
14
+ s.required_rubygems_version = ">= 1.3.6"
15
+ s.rubyforge_project = "logstash-cli"
16
+
17
+ s.add_dependency "tire"
18
+ s.add_dependency "thor"
19
+ # s.add_dependency "amqp"
20
+ s.add_dependency "rack"
21
+ s.add_dependency "yajl-ruby"
22
+ s.add_dependency "fastercsv"
23
+ s.add_dependency "json"
24
+ #s.add_dependency "amqp-utils"
25
+
26
+ s.add_development_dependency "bundler", ">= 1.0.0"
27
+
28
+ s.files = `git ls-files`.split("\n")
29
+ s.executables = `git ls-files`.split("\n").map{ |f| f =~ /^bin\/(.*)/ ? $1 : nil }.compact
30
+ s.require_path = 'lib'
31
+ end
32
+
metadata ADDED
@@ -0,0 +1,177 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: logstash-cli
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Patrick Debois
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-05-11 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
31
+ 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"
44
+ type: :runtime
45
+ name: thor
46
+ version_requirements: *id002
47
+ 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"
58
+ type: :runtime
59
+ name: rack
60
+ version_requirements: *id003
61
+ 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"
72
+ type: :runtime
73
+ name: yajl-ruby
74
+ version_requirements: *id004
75
+ 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"
86
+ type: :runtime
87
+ name: fastercsv
88
+ version_requirements: *id005
89
+ 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"
100
+ type: :runtime
101
+ name: json
102
+ version_requirements: *id006
103
+ 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: 23
111
+ segments:
112
+ - 1
113
+ - 0
114
+ - 0
115
+ version: 1.0.0
116
+ type: :development
117
+ name: bundler
118
+ version_requirements: *id007
119
+ prerelease: false
120
+ description: CLI inteface to logstash
121
+ email:
122
+ - patrick.debois@jedi.be
123
+ executables:
124
+ - logstash-cli
125
+ extensions: []
126
+
127
+ extra_rdoc_files: []
128
+
129
+ files:
130
+ - .gitignore
131
+ - .rvmrc
132
+ - Gemfile
133
+ - README.md
134
+ - bin/logstash-cli
135
+ - lib/logstash-cli.rb
136
+ - lib/logstash-cli/cli.rb
137
+ - lib/logstash-cli/command/grep.rb
138
+ - lib/logstash-cli/command/live.rb
139
+ - lib/logstash-cli/version.rb
140
+ - logstashcli.gemspec
141
+ homepage: http://github.com/jedi4ever/logstash-cli/
142
+ licenses: []
143
+
144
+ post_install_message:
145
+ rdoc_options: []
146
+
147
+ require_paths:
148
+ - lib
149
+ required_ruby_version: !ruby/object:Gem::Requirement
150
+ none: false
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ hash: 3
155
+ segments:
156
+ - 0
157
+ version: "0"
158
+ required_rubygems_version: !ruby/object:Gem::Requirement
159
+ none: false
160
+ requirements:
161
+ - - ">="
162
+ - !ruby/object:Gem::Version
163
+ hash: 23
164
+ segments:
165
+ - 1
166
+ - 3
167
+ - 6
168
+ version: 1.3.6
169
+ requirements: []
170
+
171
+ rubyforge_project: logstash-cli
172
+ rubygems_version: 1.8.12
173
+ signing_key:
174
+ specification_version: 3
175
+ summary: CLI interface to logstash
176
+ test_files: []
177
+