logstash-lite 0.2.20101222161646 → 0.2.20110112115019

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