logstash-lite 0.2.20101222161646 → 0.2.20110112115019

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/bin/logstash +13 -2
  2. data/bin/logstash-test +1 -3
  3. data/lib/logstash.rb +1 -1
  4. data/lib/logstash/agent.rb +37 -40
  5. data/lib/logstash/event.rb +20 -5
  6. data/lib/logstash/filters.rb +1 -1
  7. data/lib/logstash/filters/base.rb +6 -1
  8. data/lib/logstash/filters/date.rb +4 -0
  9. data/lib/logstash/filters/field.rb +4 -5
  10. data/lib/logstash/filters/grep.rb +39 -3
  11. data/lib/logstash/filters/grok.rb +10 -3
  12. data/lib/logstash/filters/grokdiscovery.rb +4 -1
  13. data/lib/logstash/filters/multiline.rb +6 -2
  14. data/lib/logstash/inputs.rb +6 -2
  15. data/lib/logstash/inputs/amqp.rb +5 -2
  16. data/lib/logstash/inputs/base.rb +17 -4
  17. data/lib/logstash/inputs/beanstalk.rb +5 -2
  18. data/lib/logstash/inputs/file.rb +7 -2
  19. data/lib/logstash/inputs/internal.rb +5 -2
  20. data/lib/logstash/inputs/stdin.rb +38 -0
  21. data/lib/logstash/inputs/stomp.rb +14 -12
  22. data/lib/logstash/inputs/syslog.rb +10 -5
  23. data/lib/logstash/inputs/tcp.rb +8 -3
  24. data/lib/logstash/inputs/twitter.rb +81 -0
  25. data/lib/logstash/logging.rb +4 -1
  26. data/lib/logstash/namespace.rb +0 -1
  27. data/lib/logstash/outputs.rb +1 -1
  28. data/lib/logstash/outputs/amqp.rb +9 -2
  29. data/lib/logstash/outputs/base.rb +7 -3
  30. data/lib/logstash/outputs/beanstalk.rb +4 -0
  31. data/lib/logstash/outputs/elasticsearch.rb +86 -18
  32. data/lib/logstash/outputs/gelf.rb +5 -6
  33. data/lib/logstash/outputs/internal.rb +7 -1
  34. data/lib/logstash/outputs/mongodb.rb +10 -10
  35. data/lib/logstash/outputs/nagios.rb +6 -2
  36. data/lib/logstash/outputs/stdout.rb +3 -4
  37. data/lib/logstash/outputs/stomp.rb +4 -0
  38. data/lib/logstash/outputs/tcp.rb +3 -4
  39. data/lib/logstash/outputs/websocket.rb +5 -6
  40. data/lib/logstash/ruby_fixes.rb +1 -3
  41. data/lib/logstash/stomp/handler.rb +29 -4
  42. data/lib/logstash/web/lib/elasticsearch.rb +8 -4
  43. data/lib/logstash/web/public/js/logstash.js +25 -5
  44. data/lib/logstash/web/public/ws/index.html +9 -7
  45. data/lib/logstash/web/server.rb +50 -12
  46. data/lib/logstash/web/views/search/ajax.haml +5 -2
  47. data/lib/logstash/web/views/search/results.txt.erb +10 -0
  48. metadata +7 -4
@@ -1,15 +1,17 @@
1
1
  require "logstash/filters/base"
2
-
2
+ require "logstash/namespace"
3
3
  gem "jls-grok", ">=0.2.3071"
4
4
  require "grok" # rubygem 'jls-grok'
5
5
 
6
6
  class LogStash::Filters::Grokdiscovery < LogStash::Filters::Base
7
+ public
7
8
  def initialize(config = {})
8
9
  super
9
10
 
10
11
  @discover_fields = {}
11
12
  end # def initialize
12
13
 
14
+ public
13
15
  def register
14
16
  # TODO(sissel): Make patterns files come from the config
15
17
  @config.each do |type, typeconfig|
@@ -24,6 +26,7 @@ class LogStash::Filters::Grokdiscovery < LogStash::Filters::Base
24
26
  end # @config.each
25
27
  end # def register
26
28
 
29
+ public
27
30
  def filter(event)
28
31
  # parse it with grok
29
32
  message = event.message
@@ -4,6 +4,7 @@
4
4
  #
5
5
 
6
6
  require "logstash/filters/base"
7
+ require "logstash/namespace"
7
8
 
8
9
  class LogStash::Filters::Multiline < LogStash::Filters::Base
9
10
  # The 'date' filter will take a value from your event and use it as the
@@ -46,6 +47,7 @@ class LogStash::Filters::Multiline < LogStash::Filters::Base
46
47
  # pattern: /\\$/
47
48
  # what: next
48
49
  #
50
+ public
49
51
  def initialize(config = {})
50
52
  super
51
53
 
@@ -53,6 +55,7 @@ class LogStash::Filters::Multiline < LogStash::Filters::Base
53
55
  @pending = Hash.new
54
56
  end # def initialize
55
57
 
58
+ public
56
59
  def register
57
60
  @config.each do |type, typeconfig|
58
61
  # typeconfig will be a hash containing 'pattern' and 'what'
@@ -82,10 +85,10 @@ class LogStash::Filters::Multiline < LogStash::Filters::Base
82
85
  @logger.fatal(["Invalid pattern for multiline filter on type '#{type}'",
83
86
  typeconfig, e])
84
87
  end
85
-
86
88
  end # @config.each
87
89
  end # def register
88
90
 
91
+ public
89
92
  def filter(event)
90
93
  return unless @types.member?(event.type)
91
94
  typeconfig = @types[event.type]
@@ -147,6 +150,7 @@ class LogStash::Filters::Multiline < LogStash::Filters::Base
147
150
  end # def filter
148
151
 
149
152
  # flush any pending messages
153
+ public
150
154
  def flush(source, type)
151
155
  key = [source, type]
152
156
  if @pending[key]
@@ -154,5 +158,5 @@ class LogStash::Filters::Multiline < LogStash::Filters::Base
154
158
  @pending.delete(key)
155
159
  end
156
160
  return event
157
- end
161
+ end # def flush
158
162
  end # class LogStash::Filters::Date
@@ -1,9 +1,13 @@
1
-
2
1
  require "logstash/namespace"
3
2
  require "logstash/ruby_fixes"
4
3
  require "uri"
5
4
 
6
5
  module LogStash::Inputs
6
+ # Given a URL, try to load the class that supports it.
7
+ # That is, if we have an input of "foo://blah/" then
8
+ # we will try to load logstash/inputs/foo and will
9
+ # expect a class LogStash::Inputs::Foo
10
+ public
7
11
  def self.from_url(url, type, &block)
8
12
  # Assume file paths if we start with "/"
9
13
  url = "file://#{url}" if url.start_with?("/")
@@ -12,7 +16,7 @@ module LogStash::Inputs
12
16
  # TODO(sissel): Add error handling
13
17
  # TODO(sissel): Allow plugin paths
14
18
  klass = uri.scheme.capitalize
15
- file = uri.scheme
19
+ file = uri.scheme.downcase
16
20
  require "logstash/inputs/#{file}"
17
21
  LogStash::Inputs.const_get(klass).new(uri, type, &block)
18
22
  end # def from_url
@@ -1,11 +1,13 @@
1
- require "logstash/inputs/base"
2
1
  require "amqp" # rubygem 'amqp'
2
+ require "logstash/inputs/base"
3
+ require "logstash/namespace"
3
4
  require "mq" # rubygem 'amqp'
4
5
  require "uuidtools" # rubygem 'uuidtools'
5
6
 
6
7
  class LogStash::Inputs::Amqp < LogStash::Inputs::Base
7
8
  MQTYPES = [ "fanout", "queue", "topic" ]
8
9
 
10
+ public
9
11
  def initialize(url, type, config={}, &block)
10
12
  super
11
13
 
@@ -20,8 +22,9 @@ class LogStash::Inputs::Amqp < LogStash::Inputs::Base
20
22
  if !MQTYPES.include?(@mqtype)
21
23
  raise "Invalid type '#{@mqtype}' must be one of #{MQTYPES.JOIN(", ")}"
22
24
  end
23
- end
25
+ end # def initialize
24
26
 
27
+ public
25
28
  def register
26
29
  @logger.info("Registering input #{@url}")
27
30
  @amqp = AMQP.connect(:host => @url.host)
@@ -5,6 +5,8 @@ require "uri"
5
5
 
6
6
  class LogStash::Inputs::Base
7
7
  attr_accessor :logger
8
+
9
+ public
8
10
  def initialize(url, type, config={}, &block)
9
11
  @logger = LogStash::Logger.new(STDERR)
10
12
  @url = url
@@ -13,21 +15,32 @@ class LogStash::Inputs::Base
13
15
  @callback = block
14
16
  @type = type
15
17
  @tags = []
16
- end
17
18
 
19
+ @urlopts = {}
20
+ if @url.query
21
+ @urlopts = CGI.parse(@url.query)
22
+ @urlopts.each do |k, v|
23
+ @urlopts[k] = v.last if v.is_a?(Array)
24
+ end
25
+ end
26
+ end # def initialize
27
+
28
+ public
18
29
  def register
19
30
  raise "#{self.class}#register must be overidden"
20
- end
31
+ end # def register
21
32
 
33
+ public
22
34
  def tag(newtag)
23
35
  @tags << newtag
24
- end
36
+ end # def tag
25
37
 
38
+ public
26
39
  def receive(event)
27
40
  @logger.debug(["Got event", { :url => @url, :event => event }])
28
41
  # Only override the type if it doesn't have one
29
42
  event.type = @type if !event.type
30
43
  event.tags |= @tags # set union
31
44
  @callback.call(event)
32
- end
45
+ end # def receive
33
46
  end # class LogStash::Inputs::Base
@@ -1,15 +1,18 @@
1
- require "logstash/inputs/base"
2
1
  require "em-jack"
2
+ require "logstash/inputs/base"
3
+ require "logstash/namespace"
3
4
 
4
5
  class LogStash::Inputs::Beanstalk < LogStash::Inputs::Base
6
+ public
5
7
  def initialize(url, type, config={}, &block)
6
8
  super
7
9
 
8
10
  if @url.path == "" or @url.path == "/"
9
11
  raise "must specify a tube for beanstalk output"
10
12
  end
11
- end
13
+ end # def initialize
12
14
 
15
+ public
13
16
  def register
14
17
  tube = @url.path[1..-1] # Skip leading '/'
15
18
  port = @url.port || 11300
@@ -1,22 +1,26 @@
1
- require "logstash/inputs/base"
2
1
  require "eventmachine-tail"
2
+ require "logstash/inputs/base"
3
+ require "logstash/namespace"
3
4
  require "socket" # for Socket.gethostname
4
5
 
5
6
  class LogStash::Inputs::File < LogStash::Inputs::Base
7
+ public
6
8
  def initialize(url, type, config={}, &block)
7
9
  super
8
10
 
9
11
  # Hack the hostname into the url.
10
12
  # This works since file:// urls don't generally have a host in it.
11
13
  @url.host = Socket.gethostname
12
- end
14
+ end # def initialize
13
15
 
16
+ public
14
17
  def register
15
18
  @logger.info("Registering #{@url}")
16
19
  EventMachine::FileGlobWatchTail.new(@url.path, Reader, interval=60,
17
20
  exclude=[], receiver=self)
18
21
  end # def register
19
22
 
23
+ public
20
24
  def receive(filetail, event)
21
25
  url = @url.clone
22
26
  url.path = filetail.path
@@ -31,6 +35,7 @@ class LogStash::Inputs::File < LogStash::Inputs::Base
31
35
  @callback.call(event)
32
36
  end # def receive
33
37
 
38
+ private
34
39
  class Reader < EventMachine::FileTail
35
40
  def initialize(path, receiver)
36
41
  super(path)
@@ -1,11 +1,12 @@
1
-
2
- require "logstash/inputs/base"
3
1
  require "eventmachine-tail"
2
+ require "logstash/inputs/base"
3
+ require "logstash/namespace"
4
4
  require "socket" # for Socket.gethostname
5
5
 
6
6
  class LogStash::Inputs::Internal < LogStash::Inputs::Base
7
7
  attr_reader :channel
8
8
 
9
+ public
9
10
  def initialize(url, type, config={}, &block)
10
11
  super
11
12
 
@@ -14,6 +15,7 @@ class LogStash::Inputs::Internal < LogStash::Inputs::Base
14
15
  @channel = EventMachine::Channel.new
15
16
  end
16
17
 
18
+ public
17
19
  def register
18
20
  @logger.info("Registering input #{@url}")
19
21
  @channel.subscribe do |event|
@@ -21,6 +23,7 @@ class LogStash::Inputs::Internal < LogStash::Inputs::Base
21
23
  end
22
24
  end # def register
23
25
 
26
+ public
24
27
  def receive(event)
25
28
  if !event.is_a?(LogStash::Event)
26
29
  event = LogStash::Event.new({
@@ -0,0 +1,38 @@
1
+ require "eventmachine-tail"
2
+ require "logstash/inputs/base"
3
+ require "logstash/namespace"
4
+ require "socket" # for Socket.gethostname
5
+
6
+ class LogStash::Inputs::Stdin < LogStash::Inputs::Base
7
+ public
8
+ def register
9
+ EventMachine::attach($stdin, InputHandler, self)
10
+ @url.host = Socket.gethostname
11
+ end # def register
12
+
13
+ public
14
+ def receive(event)
15
+ event = LogStash::Event.new({
16
+ "@message" => event,
17
+ "@type" => @type,
18
+ "@tags" => @tags.clone,
19
+ })
20
+ event.source = @url
21
+ @logger.debug(["Got event", event])
22
+ @callback.call(event)
23
+ end # def receive
24
+
25
+ class InputHandler < EventMachine::Connection
26
+ def initialize(obj)
27
+ @receiver = obj
28
+ end # def initialize
29
+
30
+ def receive_data(data)
31
+ @buffer ||= BufferedTokenizer.new
32
+ @buffer.extract(data).each do |line|
33
+ @receiver.receive(line)
34
+ end
35
+ end # def receive_data
36
+ end # class InputHandler
37
+
38
+ end # class LogStash::Inputs::Stdin
@@ -1,8 +1,21 @@
1
1
  require "logstash/inputs/base"
2
+ require "logstash/namespace"
2
3
  require "logstash/stomp/handler"
3
4
 
4
5
  class LogStash::Inputs::Stomp < LogStash::Inputs::Base
6
+ public
7
+ def initialize(url, config={}, &block)
8
+ super
9
+ @logger.debug(["Connecting", { :url => @url }])
10
+ end # def initialize
11
+
12
+ public
13
+ def register
14
+ @logger.info(["Registering input", { :url => @url}])
15
+ EventMachine::connect(@url.host, @url.port, InputHandler, self, @logger, @url)
16
+ end # def register
5
17
 
18
+ private
6
19
  class InputHandler < LogStash::Stomp::Handler
7
20
  def receive_msg(message)
8
21
  super
@@ -13,15 +26,4 @@ class LogStash::Inputs::Stomp < LogStash::Inputs::Base
13
26
  end
14
27
  end # def receive_msg
15
28
  end # class StompHandler
16
-
17
- def initialize(url, config={}, &block)
18
- super
19
-
20
- @logger.debug(["Connecting", { :url => @url }])
21
- end # def initialize
22
-
23
- def register
24
- @logger.info(["Registering input", { :url => @url}])
25
- EventMachine::connect(@url.host, @url.port, InputHandler, self, @logger, @url)
26
- end # def register
27
- end # class LogStash::Inputs::Amqp
29
+ end # class LogStash::Inputs::Stomp
@@ -1,11 +1,12 @@
1
- require "logstash/inputs/base"
2
- require "eventmachine-tail"
3
- require "socket" # for Socket.gethostname
4
1
  require "date"
2
+ require "eventmachine-tail"
3
+ require "logstash/inputs/base"
4
+ require "logstash/namespace"
5
5
  require "logstash/time" # should really use the filters/date.rb bits
6
-
6
+ require "socket" # for Socket.gethostname
7
7
 
8
8
  class LogStash::Inputs::Syslog < LogStash::Inputs::Base
9
+ public
9
10
  def register
10
11
  if !@url.host or !@url.port
11
12
  @logger.fatal("No host or port given in #{self.class}: #{@url}")
@@ -26,6 +27,7 @@ class LogStash::Inputs::Syslog < LogStash::Inputs::Base
26
27
  #<priority timestamp Mmm dd hh:mm:ss host msg
27
28
  end # def register
28
29
 
30
+ public
29
31
  def receive(host, port, message)
30
32
  url = @url.clone
31
33
  url.host = host
@@ -48,6 +50,7 @@ class LogStash::Inputs::Syslog < LogStash::Inputs::Base
48
50
  # If the message cannot be recognized (see @@syslog_re), we'll
49
51
  # treat it like the whole event.message is correct and try to fill
50
52
  # the missing pieces (host, priority, etc)
53
+ public
51
54
  def syslog_relay(event, url)
52
55
  match = @@syslog_re.match(event.message)
53
56
  if match
@@ -90,6 +93,7 @@ class LogStash::Inputs::Syslog < LogStash::Inputs::Base
90
93
  end
91
94
  end # def syslog_relay
92
95
 
96
+ private
93
97
  class TCPInput < EventMachine::Connection
94
98
  def initialize(receiver, logger)
95
99
  @logger = logger
@@ -107,6 +111,7 @@ class LogStash::Inputs::Syslog < LogStash::Inputs::Base
107
111
  end # def receive_data
108
112
  end # class TCPInput
109
113
 
114
+ private
110
115
  class UDPInput < EventMachine::Connection
111
116
  def initialize(receiver, logger)
112
117
  @logger = logger
@@ -120,4 +125,4 @@ class LogStash::Inputs::Syslog < LogStash::Inputs::Base
120
125
  @receiver.receive(host, port, data.chomp)
121
126
  end # def receive_data
122
127
  end # class UDPInput
123
- end # class LogStash::Inputs::Tcp
128
+ end # class LogStash::Inputs::Syslog
@@ -1,12 +1,15 @@
1
- require "logstash/inputs/base"
2
1
  require "eventmachine-tail"
2
+ require "logstash/inputs/base"
3
+ require "logstash/namespace"
3
4
  require "socket" # for Socket.gethostname
4
5
 
5
6
  class LogStash::Inputs::Tcp < LogStash::Inputs::Base
7
+ public
6
8
  def initialize(url, type, config={}, &block)
7
9
  super
8
- end
10
+ end # def initialize
9
11
 
12
+ public
10
13
  def register
11
14
  if !@url.host or !@url.port
12
15
  @logger.fatal("No host or port given in #{self.class}: #{@url}")
@@ -16,8 +19,9 @@ class LogStash::Inputs::Tcp < LogStash::Inputs::Base
16
19
 
17
20
  @logger.info("Starting tcp listener for #{@url}")
18
21
  EventMachine::start_server(@url.host, @url.port, TCPInput, @url, self, @logger)
19
- end
22
+ end # def register
20
23
 
24
+ public
21
25
  def receive(host, port, event)
22
26
  url = @url.clone
23
27
  url.host = host
@@ -33,6 +37,7 @@ class LogStash::Inputs::Tcp < LogStash::Inputs::Base
33
37
  @callback.call(event)
34
38
  end # def receive
35
39
 
40
+ private
36
41
  class TCPInput < EventMachine::Connection
37
42
  def initialize(url, receiver, logger)
38
43
  @logger = logger
@@ -0,0 +1,81 @@
1
+ require "cgi"
2
+ require "em-http-request"
3
+ require "logstash/inputs/base"
4
+ require "logstash/namespace"
5
+
6
+ # TODO(sissel): This could use some refactoring.
7
+ class LogStash::Inputs::Twitter < LogStash::Inputs::Base
8
+ public
9
+ def register
10
+ api_url = "https://stream.twitter.com/1/statuses/filter.json"
11
+ @http = EventMachine::HttpRequest.new(api_url)
12
+ @logger.info(["Registering input", { :url => @url, :api_url => api_url, :params => @urlopts }])
13
+ source = "twitter://...#{@url.path}?#{@url.query}"
14
+
15
+ if @url.user.nil? or @user.password.nil?
16
+ message = "User and password missing for twitter input #{@url.to_s}")
17
+ @logger.error(message)
18
+ raise message
19
+ end
20
+
21
+ req = nil
22
+ connect = proc do
23
+ @logger.info(["Connecting", { :url => @url, :api_url => api_url, :params => @urlopts}])
24
+ req = @http.post :body => @urlopts,
25
+ :head => { "Authorization" => [ @url.user, @url.password ] }
26
+ buffer = BufferedTokenizer.new
27
+
28
+ req.stream do |chunk|
29
+ buffer.extract(chunk).each do |line|
30
+ begin
31
+ tweet = JSON.parse(line)
32
+ rescue JSON::ParserError => e
33
+ @logger.warn("Invalid JSON, aborting connection: #{line}")
34
+ req.errback
35
+ next
36
+ end
37
+ next if !tweet
38
+
39
+ event = LogStash::Event.new({
40
+ "@message" => tweet["text"],
41
+ "@type" => @type,
42
+ "@tags" => @tags.clone,
43
+ })
44
+
45
+ event.fields.merge!(
46
+ "user" => (tweet["user"]["screen_name"] rescue nil),
47
+ "client" => (tweet["user"]["source"] rescue nil),
48
+ "retweeted" => (tweet["retweeted"] rescue nil)
49
+ )
50
+
51
+ event.fields["in-reply-to"] = tweet["in_reply_to_status_id"] if tweet["in_reply_to_status_id"]
52
+
53
+ urls = tweet["entities"]["urls"] rescue []
54
+ if urls.size > 0
55
+ event.fields["urls"] = urls.collect { |u| u["url"] }
56
+ end
57
+
58
+ event.source = source
59
+ @logger.debug(["Got event", event])
60
+ @callback.call(event)
61
+ end # buffer.extract
62
+ end # req.stream
63
+
64
+ req.errback do
65
+ @logger.warn(["Error occurred, not sure what, seriously. Reconnecting!", { :url => @url }])
66
+
67
+ req.close_connection() rescue nil
68
+
69
+ EventMachine::Timer.new(60) do
70
+ connect.call
71
+ end
72
+ end # req.errback
73
+
74
+ req.callback do
75
+ @logger.warn(["Request completed. Unexpected!", { :url => @url }])
76
+ end
77
+ end # connect = proc do
78
+
79
+ connect.call
80
+ end # def register
81
+ end # class LogStash::Inputs::Twitter