logstash-lite 0.2.20101118134500

Sign up to get free protection for your applications and to get access to all the features.
Files changed (134) hide show
  1. data/bin/logstash +56 -0
  2. data/bin/logstash-web +6 -0
  3. data/etc/logstash-elasticsearch-rabbitmq-river.yaml +41 -0
  4. data/etc/logstash-mongodb-storage.yaml +5 -0
  5. data/etc/logstash-parser.yaml +20 -0
  6. data/etc/logstash-reader.yaml +8 -0
  7. data/etc/logstash-shipper.yaml +18 -0
  8. data/etc/logstash-standalone.yaml +47 -0
  9. data/etc/prod.yaml +38 -0
  10. data/etc/redhat/logstash +92 -0
  11. data/etc/redhat/logstash-agent +83 -0
  12. data/etc/redhat/logstash-agent.sysconfig +7 -0
  13. data/etc/redhat/logstash.spec +171 -0
  14. data/etc/redhat/logstash.sysconfig +18 -0
  15. data/etc/tograylog.yaml +37 -0
  16. data/examples/test.rb +38 -0
  17. data/lib/logstash.rb +3 -0
  18. data/lib/logstash/agent.rb +116 -0
  19. data/lib/logstash/event.rb +70 -0
  20. data/lib/logstash/filters.rb +17 -0
  21. data/lib/logstash/filters/base.rb +17 -0
  22. data/lib/logstash/filters/date.rb +59 -0
  23. data/lib/logstash/filters/field.rb +29 -0
  24. data/lib/logstash/filters/grok.rb +74 -0
  25. data/lib/logstash/filters/grokdiscovery.rb +60 -0
  26. data/lib/logstash/inputs.rb +18 -0
  27. data/lib/logstash/inputs/amqp.rb +48 -0
  28. data/lib/logstash/inputs/base.rb +32 -0
  29. data/lib/logstash/inputs/file.rb +47 -0
  30. data/lib/logstash/inputs/syslog.rb +123 -0
  31. data/lib/logstash/inputs/tcp.rb +51 -0
  32. data/lib/logstash/logging.rb +82 -0
  33. data/lib/logstash/namespace.rb +6 -0
  34. data/lib/logstash/outputs.rb +15 -0
  35. data/lib/logstash/outputs/amqp.rb +48 -0
  36. data/lib/logstash/outputs/base.rb +29 -0
  37. data/lib/logstash/outputs/elasticsearch.rb +71 -0
  38. data/lib/logstash/outputs/gelf.rb +35 -0
  39. data/lib/logstash/outputs/mongodb.rb +19 -0
  40. data/lib/logstash/outputs/stdout.rb +15 -0
  41. data/lib/logstash/outputs/websocket.rb +35 -0
  42. data/lib/logstash/time.rb +27 -0
  43. data/lib/logstash/web/lib/elasticsearch.rb +79 -0
  44. data/lib/logstash/web/public/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  45. data/lib/logstash/web/public/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  46. data/lib/logstash/web/public/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  47. data/lib/logstash/web/public/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  48. data/lib/logstash/web/public/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  49. data/lib/logstash/web/public/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  50. data/lib/logstash/web/public/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  51. data/lib/logstash/web/public/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  52. data/lib/logstash/web/public/css/smoothness/images/ui-icons_222222_256x240.png +0 -0
  53. data/lib/logstash/web/public/css/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  54. data/lib/logstash/web/public/css/smoothness/images/ui-icons_454545_256x240.png +0 -0
  55. data/lib/logstash/web/public/css/smoothness/images/ui-icons_888888_256x240.png +0 -0
  56. data/lib/logstash/web/public/css/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  57. data/lib/logstash/web/public/css/smoothness/jquery-ui-1.8.5.custom.css +572 -0
  58. data/lib/logstash/web/public/js/flot/API.txt +1024 -0
  59. data/lib/logstash/web/public/js/flot/FAQ.txt +71 -0
  60. data/lib/logstash/web/public/js/flot/LICENSE.txt +22 -0
  61. data/lib/logstash/web/public/js/flot/Makefile +15 -0
  62. data/lib/logstash/web/public/js/flot/NEWS.txt +340 -0
  63. data/lib/logstash/web/public/js/flot/PLUGINS.txt +105 -0
  64. data/lib/logstash/web/public/js/flot/README.txt +81 -0
  65. data/lib/logstash/web/public/js/flot/examples/ajax.html +143 -0
  66. data/lib/logstash/web/public/js/flot/examples/annotating.html +75 -0
  67. data/lib/logstash/web/public/js/flot/examples/arrow-down.gif +0 -0
  68. data/lib/logstash/web/public/js/flot/examples/arrow-left.gif +0 -0
  69. data/lib/logstash/web/public/js/flot/examples/arrow-right.gif +0 -0
  70. data/lib/logstash/web/public/js/flot/examples/arrow-up.gif +0 -0
  71. data/lib/logstash/web/public/js/flot/examples/basic.html +38 -0
  72. data/lib/logstash/web/public/js/flot/examples/data-eu-gdp-growth-1.json +4 -0
  73. data/lib/logstash/web/public/js/flot/examples/data-eu-gdp-growth-2.json +4 -0
  74. data/lib/logstash/web/public/js/flot/examples/data-eu-gdp-growth-3.json +4 -0
  75. data/lib/logstash/web/public/js/flot/examples/data-eu-gdp-growth-4.json +4 -0
  76. data/lib/logstash/web/public/js/flot/examples/data-eu-gdp-growth-5.json +4 -0
  77. data/lib/logstash/web/public/js/flot/examples/data-eu-gdp-growth.json +4 -0
  78. data/lib/logstash/web/public/js/flot/examples/data-japan-gdp-growth.json +4 -0
  79. data/lib/logstash/web/public/js/flot/examples/data-usa-gdp-growth.json +4 -0
  80. data/lib/logstash/web/public/js/flot/examples/dual-axis.html +39 -0
  81. data/lib/logstash/web/public/js/flot/examples/graph-types.html +75 -0
  82. data/lib/logstash/web/public/js/flot/examples/hs-2004-27-a-large_web.jpg +0 -0
  83. data/lib/logstash/web/public/js/flot/examples/image.html +45 -0
  84. data/lib/logstash/web/public/js/flot/examples/index.html +43 -0
  85. data/lib/logstash/web/public/js/flot/examples/interacting.html +93 -0
  86. data/lib/logstash/web/public/js/flot/examples/layout.css +6 -0
  87. data/lib/logstash/web/public/js/flot/examples/navigate.html +118 -0
  88. data/lib/logstash/web/public/js/flot/examples/selection.html +114 -0
  89. data/lib/logstash/web/public/js/flot/examples/setting-options.html +65 -0
  90. data/lib/logstash/web/public/js/flot/examples/stacking.html +77 -0
  91. data/lib/logstash/web/public/js/flot/examples/thresholding.html +54 -0
  92. data/lib/logstash/web/public/js/flot/examples/time.html +71 -0
  93. data/lib/logstash/web/public/js/flot/examples/tracking.html +95 -0
  94. data/lib/logstash/web/public/js/flot/examples/turning-series.html +98 -0
  95. data/lib/logstash/web/public/js/flot/examples/visitors.html +90 -0
  96. data/lib/logstash/web/public/js/flot/examples/zooming.html +98 -0
  97. data/lib/logstash/web/public/js/flot/excanvas.js +1427 -0
  98. data/lib/logstash/web/public/js/flot/excanvas.min.js +1 -0
  99. data/lib/logstash/web/public/js/flot/jquery.colorhelpers.js +174 -0
  100. data/lib/logstash/web/public/js/flot/jquery.colorhelpers.min.js +1 -0
  101. data/lib/logstash/web/public/js/flot/jquery.flot.crosshair.js +156 -0
  102. data/lib/logstash/web/public/js/flot/jquery.flot.crosshair.min.js +1 -0
  103. data/lib/logstash/web/public/js/flot/jquery.flot.image.js +237 -0
  104. data/lib/logstash/web/public/js/flot/jquery.flot.image.min.js +1 -0
  105. data/lib/logstash/web/public/js/flot/jquery.flot.js +2119 -0
  106. data/lib/logstash/web/public/js/flot/jquery.flot.min.js +1 -0
  107. data/lib/logstash/web/public/js/flot/jquery.flot.navigate.js +272 -0
  108. data/lib/logstash/web/public/js/flot/jquery.flot.navigate.min.js +1 -0
  109. data/lib/logstash/web/public/js/flot/jquery.flot.selection.js +299 -0
  110. data/lib/logstash/web/public/js/flot/jquery.flot.selection.min.js +1 -0
  111. data/lib/logstash/web/public/js/flot/jquery.flot.stack.js +152 -0
  112. data/lib/logstash/web/public/js/flot/jquery.flot.stack.min.js +1 -0
  113. data/lib/logstash/web/public/js/flot/jquery.flot.threshold.js +103 -0
  114. data/lib/logstash/web/public/js/flot/jquery.flot.threshold.min.js +1 -0
  115. data/lib/logstash/web/public/js/flot/jquery.js +4376 -0
  116. data/lib/logstash/web/public/js/flot/jquery.min.js +19 -0
  117. data/lib/logstash/web/public/js/jquery-hashchange-1.0.0.js +121 -0
  118. data/lib/logstash/web/public/js/jquery.livequery.js +250 -0
  119. data/lib/logstash/web/public/js/jquery.tmpl.min.js +1 -0
  120. data/lib/logstash/web/public/js/logstash.js +202 -0
  121. data/lib/logstash/web/server.rb +90 -0
  122. data/lib/logstash/web/views/header.haml +8 -0
  123. data/lib/logstash/web/views/layout.haml +21 -0
  124. data/lib/logstash/web/views/main/index.haml +5 -0
  125. data/lib/logstash/web/views/search/ajax.haml +32 -0
  126. data/lib/logstash/web/views/search/results.haml +17 -0
  127. data/lib/logstash/web/views/style.sass +50 -0
  128. data/patterns/firewalls +2 -0
  129. data/patterns/grok-patterns +90 -0
  130. data/patterns/haproxy +5 -0
  131. data/patterns/linux-syslog +7 -0
  132. data/patterns/nagios +7 -0
  133. data/patterns/ruby +2 -0
  134. metadata +228 -0
@@ -0,0 +1,60 @@
1
+ require "logstash/filters/base"
2
+
3
+ gem "jls-grok", ">=0.2.3071"
4
+ require "grok" # rubygem 'jls-grok'
5
+
6
+ class LogStash::Filters::Grokdiscovery < LogStash::Filters::Base
7
+ def initialize(config = {})
8
+ super
9
+
10
+ @discover_fields = {}
11
+ end # def initialize
12
+
13
+ def register
14
+ # TODO(sissel): Make patterns files come from the config
15
+ @config.each do |type, typeconfig|
16
+ @logger.debug("Registering type with grok: #{type}")
17
+ @grok = Grok.new
18
+ Dir.glob("patterns/*").each do |path|
19
+ @grok.add_patterns_from_file(path)
20
+ end
21
+ @discover_fields[type] = typeconfig
22
+ @logger.debug(["Enabling discovery", { :type => type, :fields => typeconfig }])
23
+ @logger.warn(@discover_fields)
24
+ end # @config.each
25
+ end # def register
26
+
27
+ def filter(event)
28
+ # parse it with grok
29
+ message = event.message
30
+ match = false
31
+
32
+ if event.type and @discover_fields.include?(event.type)
33
+ discover = @discover_fields[event.type] & event.fields.keys
34
+ discover.each do |field|
35
+ value = event.fields[field]
36
+ value = [value] if value.is_a?(String)
37
+
38
+ value.each do |v|
39
+ pattern = @grok.discover(v)
40
+ @logger.warn("Trying #{v} => #{pattern}")
41
+ @grok.compile(pattern)
42
+ match = @grok.match(v)
43
+ if match
44
+ @logger.warn(["Match", match.captures])
45
+ event.fields.merge!(match.captures) do |key, oldval, newval|
46
+ @logger.warn(["Merging #{key}", oldval, newval])
47
+ oldval + newval # should both be arrays...
48
+ end
49
+ else
50
+ @logger.warn(["Discovery produced something not matchable?", { :input => v }])
51
+ end
52
+ end # value.each
53
+ end # discover.each
54
+ else
55
+ @logger.info("Unknown type for #{event.source} (type: #{event.type})")
56
+ @logger.debug(event.to_hash)
57
+ end
58
+ @logger.debug(["Event now: ", event.to_hash])
59
+ end # def filter
60
+ end # class LogStash::Filters::Grokdiscovery
@@ -0,0 +1,18 @@
1
+
2
+ require "logstash/namespace"
3
+ require "uri"
4
+
5
+ module LogStash::Inputs
6
+ def self.from_url(url, type, &block)
7
+ # Assume file paths if we start with "/"
8
+ url = "file://#{url}" if url.start_with?("/")
9
+
10
+ uri = URI.parse(url)
11
+ # TODO(sissel): Add error handling
12
+ # TODO(sissel): Allow plugin paths
13
+ klass = uri.scheme.capitalize
14
+ file = uri.scheme
15
+ require "logstash/inputs/#{file}"
16
+ LogStash::Inputs.const_get(klass).new(uri, type, &block)
17
+ end # def from_url
18
+ end # module LogStash::Inputs
@@ -0,0 +1,48 @@
1
+ require "logstash/inputs/base"
2
+ require "amqp" # rubygem 'amqp'
3
+ require "mq" # rubygem 'amqp'
4
+ require "uuidtools" # rubygem 'uuidtools'
5
+
6
+ class LogStash::Inputs::Amqp < LogStash::Inputs::Base
7
+ MQTYPES = [ "fanout", "queue", "topic" ]
8
+
9
+ def initialize(url, type, config={}, &block)
10
+ super
11
+
12
+ @mq = nil
13
+
14
+ # Handle path /<type>/<name>
15
+ unused, @mqtype, @name = @url.path.split("/", 3)
16
+ if @mqtype == nil or @name == nil
17
+ raise "amqp urls must have a path of /<type>/name where <type> is #{MQTYPES.join(", ")}"
18
+ end
19
+
20
+ if !MQTYPES.include?(@mqtype)
21
+ raise "Invalid type '#{@mqtype}' must be one of #{MQTYPES.JOIN(", ")}"
22
+ end
23
+ end
24
+
25
+ def register
26
+ @logger.info("Registering #{@url}")
27
+ @amqp = AMQP.connect(:host => @url.host)
28
+ @mq = MQ.new(@amqp)
29
+ @target = nil
30
+
31
+ @target = @mq.queue(UUIDTools::UUID.timestamp_create)
32
+ case @mqtype
33
+ when "fanout"
34
+ #@target.bind(MQ.fanout(@url.path, :durable => true))
35
+ @target.bind(@mq.fanout(@name))
36
+ when "direct"
37
+ @target.bind(@mq.direct(@name))
38
+ when "topic"
39
+ @target.bind(@mq.topic(@name))
40
+ end # case @mqtype
41
+
42
+ @target.subscribe(:ack => true) do |header, message|
43
+ event = LogStash::Event.from_json(message)
44
+ receive(event)
45
+ header.ack
46
+ end
47
+ end # def register
48
+ end # class LogStash::Inputs::Amqp
@@ -0,0 +1,32 @@
1
+ require "logstash/namespace"
2
+ require "logstash/event"
3
+ require "logstash/logging"
4
+ require "uri"
5
+
6
+ class LogStash::Inputs::Base
7
+ def initialize(url, type, config={}, &block)
8
+ @logger = LogStash::Logger.new(STDERR)
9
+ @url = url
10
+ @url = URI.parse(url) if url.is_a? String
11
+ @config = config
12
+ @callback = block
13
+ @type = type
14
+ @tags = []
15
+ end
16
+
17
+ def register
18
+ raise "#{self.class}#register must be overidden"
19
+ end
20
+
21
+ def tag(newtag)
22
+ @tags << newtag
23
+ end
24
+
25
+ def receive(event)
26
+ @logger.debug(["Got event", { :url => @url, :event => event }])
27
+ # Only override the type if it doesn't have one
28
+ event.type = @type if !event.type
29
+ event.tags |= @tags # set union
30
+ @callback.call(event)
31
+ end
32
+ end # class LogStash::Inputs::Base
@@ -0,0 +1,47 @@
1
+ require "logstash/inputs/base"
2
+ require "eventmachine-tail"
3
+ require "socket" # for Socket.gethostname
4
+
5
+ class LogStash::Inputs::File < LogStash::Inputs::Base
6
+ def initialize(url, type, config={}, &block)
7
+ super
8
+
9
+ # Hack the hostname into the url.
10
+ # This works since file:// urls don't generally have a host in it.
11
+ @url.host = Socket.gethostname
12
+ end
13
+
14
+ def register
15
+ EventMachine::FileGlobWatchTail.new(@url.path, Reader, interval=60,
16
+ exclude=[], receiver=self)
17
+ end # def register
18
+
19
+ def receive(filetail, event)
20
+ url = @url.clone
21
+ url.path = filetail.path
22
+ @logger.debug(["original url", { :originalurl => @url, :newurl => url }])
23
+ event = LogStash::Event.new({
24
+ "@source" => url,
25
+ "@message" => event,
26
+ "@type" => @type,
27
+ "@tags" => @tags.clone,
28
+ })
29
+ @logger.debug(["Got event", event])
30
+ @callback.call(event)
31
+ end # def receive
32
+
33
+ class Reader < EventMachine::FileTail
34
+ def initialize(path, receiver)
35
+ super(path)
36
+ @receiver = receiver
37
+ @buffer = BufferedTokenizer.new # From eventmachine
38
+ end
39
+
40
+ def receive_data(data)
41
+ # TODO(2.0): Support multiline log data
42
+ @buffer.extract(data).each do |line|
43
+ @receiver.receive(self, line)
44
+ end
45
+ end # def receive_data
46
+ end # class Reader
47
+ end # class LogStash::Inputs::File
@@ -0,0 +1,123 @@
1
+ require "logstash/inputs/base"
2
+ require "eventmachine-tail"
3
+ require "socket" # for Socket.gethostname
4
+ require "date"
5
+ require "logstash/time" # should really use the filters/date.rb bits
6
+
7
+
8
+ class LogStash::Inputs::Syslog < LogStash::Inputs::Base
9
+ def register
10
+ if !@url.host or !@url.port
11
+ @logger.fatal("No host or port given in #{self.class}: #{@url}")
12
+ # TODO(sissel): Make this an actual exception class
13
+ raise "configuration error"
14
+ end
15
+
16
+ @logger.info("Starting tcp listener for #{@url}")
17
+ EventMachine::start_server(@url.host, @url.port, TCPInput, self, @logger)
18
+
19
+ @logger.info("Starting udp listener for #{@url}")
20
+ EventMachine::open_datagram_socket(@url.host, @url.port, UDPInput, self,
21
+ @logger)
22
+
23
+ # This comes from RFC3164, mostly.
24
+ @@syslog_re ||= \
25
+ /<([0-9]{1,3})>([A-z]{3} [0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}) (\S+) (.*)/
26
+ #<priority timestamp Mmm dd hh:mm:ss host msg
27
+ end # def register
28
+
29
+ def receive(host, port, message)
30
+ url = @url.clone
31
+ url.host = host
32
+ url.port = port
33
+
34
+ # Do some syslog relay-like behavior.
35
+ # * Add syslog headers if there are none
36
+ event = LogStash::Event.new({
37
+ "@message" => message,
38
+ "@type" => @type,
39
+ "@tags" => @tags.clone,
40
+ })
41
+ syslog_relay(event, url)
42
+ @logger.debug(["Got event", event.class, event.to_hash])
43
+ @callback.call(event)
44
+ end # def receive
45
+
46
+ # Following RFC3164 where sane, we'll try to parse a received message
47
+ # as if you were relaying a syslog message to it.
48
+ # If the message cannot be recognized (see @@syslog_re), we'll
49
+ # treat it like the whole event.message is correct and try to fill
50
+ # the missing pieces (host, priority, etc)
51
+ def syslog_relay(event, url)
52
+ match = @@syslog_re.match(event.message)
53
+ if match
54
+ # match[1,2,3,4] = {pri, timestamp, hostname, message}
55
+ # Per RFC3164, priority = (facility * 8) + severity
56
+ # = (facility << 3) & (severity)
57
+ priority = match[1].to_i
58
+ severity = priority & 7 # 7 is 111 (3 bits)
59
+ facility = priority >> 3
60
+ event.fields["priority"] = priority
61
+ event.fields["severity"] = severity
62
+ event.fields["facility"] = facility
63
+
64
+ # TODO(sissel): Use the date filter, somehow.
65
+ event.timestamp = LogStash::Time.to_iso8601(
66
+ DateTime.strptime(match[2], "%b %d %H:%M:%S"))
67
+
68
+ # At least the hostname is simple...
69
+ url.host = match[3]
70
+ url.port = nil
71
+ event.source = url
72
+
73
+ event.message = match[4]
74
+ else
75
+ @logger.info(["NOT SYSLOG", event.message])
76
+ url.host = Socket.gethostname if url.host == "127.0.0.1"
77
+
78
+ # RFC3164 says unknown messages get pri=13
79
+ priority = 13
80
+ severity = priority & 7 # 7 is 111 (3 bits)
81
+ facility = priority >> 3
82
+ event.fields["priority"] = 13
83
+ event.fields["severity"] = 5 # 13 & 7 == 5
84
+ event.fields["facility"] = 1 # 13 >> 3 == 1
85
+
86
+ # Don't need to modify the message, here.
87
+ # event.message = ...
88
+
89
+ event.source = url
90
+ end
91
+ end # def syslog_relay
92
+
93
+ class TCPInput < EventMachine::Connection
94
+ def initialize(receiver, logger)
95
+ @logger = logger
96
+ @receiver = receiver
97
+ @buffer = BufferedTokenizer.new # From eventmachine
98
+ end # def initialize
99
+
100
+ # Messages over TCP may not be received all at once, chunk by newline.
101
+ def receive_data(data)
102
+ @buffer.extract(data).each do |line|
103
+ port, host = Socket.unpack_sockaddr_in(self.get_peername)
104
+ # Trim trailing newlines
105
+ @receiver.receive(host, port, line.chomp)
106
+ end
107
+ end # def receive_data
108
+ end # class TCPInput
109
+
110
+ class UDPInput < EventMachine::Connection
111
+ def initialize(receiver, logger)
112
+ @logger = logger
113
+ @receiver = receiver
114
+ end # def initialize
115
+
116
+ # Every udp packet is a unique message.
117
+ def receive_data(data)
118
+ port, host = Socket.unpack_sockaddr_in(self.get_peername)
119
+ # Trim trailing newlines
120
+ @receiver.receive(host, port, data.chomp)
121
+ end # def receive_data
122
+ end # class UDPInput
123
+ end # class LogStash::Inputs::Tcp
@@ -0,0 +1,51 @@
1
+ require "logstash/inputs/base"
2
+ require "eventmachine-tail"
3
+ require "socket" # for Socket.gethostname
4
+
5
+ class LogStash::Inputs::Tcp < LogStash::Inputs::Base
6
+ def initialize(url, type, config={}, &block)
7
+ super
8
+ end
9
+
10
+ def register
11
+ if !@url.host or !@url.port
12
+ @logger.fatal("No host or port given in #{self.class}: #{@url}")
13
+ # TODO(sissel): Make this an actual exception class
14
+ raise "configuration error"
15
+ end
16
+
17
+ @logger.info("Starting tcp listener for #{@url}")
18
+ EventMachine::start_server(@url.host, @url.port, TCPInput, @url, self, @logger)
19
+ end
20
+
21
+ def receive(host, port, event)
22
+ url = @url.clone
23
+ url.host = host
24
+ url.port = port
25
+ @logger.debug(["original url", { :originalurl => @url, :newurl => url }])
26
+ event = LogStash::Event.new({
27
+ "@source" => url,
28
+ "@message" => event,
29
+ "@type" => @type,
30
+ "@tags" => @tags.clone,
31
+ })
32
+ @logger.debug(["Got event", event])
33
+ @callback.call(event)
34
+ end # def receive
35
+
36
+ class TCPInput < EventMachine::Connection
37
+ def initialize(url, receiver, logger)
38
+ @logger = logger
39
+ @receiver = receiver
40
+ @url = url;
41
+ @buffer = BufferedTokenizer.new # From eventmachine
42
+ end # def initialize
43
+
44
+ def receive_data(data)
45
+ @buffer.extract(data).each do |line|
46
+ port, host = Socket.unpack_sockaddr_in(self.get_peername)
47
+ @receiver.receive(host, port, line)
48
+ end
49
+ end # def receive_data
50
+ end # class TCPInput
51
+ end # class LogStash::Inputs::Tcp
@@ -0,0 +1,82 @@
1
+ require "logstash/namespace"
2
+ require "logger"
3
+
4
+ class LogStash::Logger < Logger
5
+ # Try to load awesome_print, if it fails, log it later
6
+ # but otherwise we should continue to operate as normal.
7
+ begin
8
+ require "ap"
9
+ @@have_awesome_print = true
10
+ rescue LoadError => e
11
+ @@have_awesome_print = false
12
+ @@notify_awesome_print_load_failed = e
13
+ end
14
+
15
+ def initialize(*args)
16
+ super(*args)
17
+ @formatter = LogStash::Logger::Formatter.new
18
+
19
+ # Set default loglevel to WARN unless $DEBUG is set (run with 'ruby -d')
20
+ self.level = $DEBUG ? Logger::DEBUG: Logger::INFO
21
+
22
+ # Conditional support for awesome_print
23
+ if !@@have_awesome_print && @@notify_awesome_print_load_failed
24
+ info [ "Failed: require 'ap' (aka awesome_print); some " \
25
+ "logging features may be disabled",
26
+ @@notify_awesome_print_load_failed ]
27
+ @@notify_awesome_print_load_failed = nil
28
+ end
29
+
30
+ @formatter.progname = self.progname = File.basename($0)
31
+ end # def initialize
32
+
33
+ def level=(level)
34
+ super(level)
35
+ @formatter.level = level
36
+ end # def level=
37
+ end # class LogStash::Logger
38
+
39
+ # Implement a custom Logger::Formatter that uses awesome_inspect on non-strings.
40
+ class LogStash::Logger::Formatter < Logger::Formatter
41
+ attr_accessor :level
42
+ attr_accessor :progname
43
+
44
+ def call(severity, timestamp, who, object)
45
+ # override progname to be the caller if the log level threshold is DEBUG
46
+ # We only do this if the logger level is DEBUG because inspecting the
47
+ # stack and doing extra string manipulation can have performance impacts
48
+ # under high logging rates.
49
+ if @level == Logger::DEBUG
50
+ # callstack inspection, include our caller
51
+ # turn this: "/usr/lib/ruby/1.8/irb/workspace.rb:52:in `irb_binding'"
52
+ # into this: ["/usr/lib/ruby/1.8/irb/workspace.rb", "52", "irb_binding"]
53
+ #
54
+ # caller[3] is actually who invoked the Logger#<type>
55
+ # This only works if you use the severity methods
56
+ path, line, method = caller[3].split(/(?::in `|:|')/)
57
+ # Trim RUBYLIB path from 'file' if we can
58
+ #whence = $:.select { |p| path.start_with?(p) }[0]
59
+ whence = $:.detect { |p| path.start_with?(p) }
60
+ if !whence
61
+ # We get here if the path is not in $:
62
+ file = path
63
+ else
64
+ file = path[whence.length + 1..-1]
65
+ end
66
+ who = "#{file}:#{line}##{method}"
67
+ end
68
+
69
+ # Log like normal if we got a string.
70
+ if object.is_a?(String)
71
+ super(severity, timestamp, who, object)
72
+ else
73
+ # If we logged an object, use .awesome_inspect (or just .inspect)
74
+ # to stringify it for higher sanity logging.
75
+ if object.respond_to?(:awesome_inspect)
76
+ super(severity, timestamp, who, object.awesome_inspect)
77
+ else
78
+ super(severity, timestamp, who, object.inspect)
79
+ end
80
+ end
81
+ end # def call
82
+ end # class LogStash::Logger::Formatter