logstash-lite 0.2.20101118134500
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/logstash +56 -0
- data/bin/logstash-web +6 -0
- data/etc/logstash-elasticsearch-rabbitmq-river.yaml +41 -0
- data/etc/logstash-mongodb-storage.yaml +5 -0
- data/etc/logstash-parser.yaml +20 -0
- data/etc/logstash-reader.yaml +8 -0
- data/etc/logstash-shipper.yaml +18 -0
- data/etc/logstash-standalone.yaml +47 -0
- data/etc/prod.yaml +38 -0
- data/etc/redhat/logstash +92 -0
- data/etc/redhat/logstash-agent +83 -0
- data/etc/redhat/logstash-agent.sysconfig +7 -0
- data/etc/redhat/logstash.spec +171 -0
- data/etc/redhat/logstash.sysconfig +18 -0
- data/etc/tograylog.yaml +37 -0
- data/examples/test.rb +38 -0
- data/lib/logstash.rb +3 -0
- data/lib/logstash/agent.rb +116 -0
- data/lib/logstash/event.rb +70 -0
- data/lib/logstash/filters.rb +17 -0
- data/lib/logstash/filters/base.rb +17 -0
- data/lib/logstash/filters/date.rb +59 -0
- data/lib/logstash/filters/field.rb +29 -0
- data/lib/logstash/filters/grok.rb +74 -0
- data/lib/logstash/filters/grokdiscovery.rb +60 -0
- data/lib/logstash/inputs.rb +18 -0
- data/lib/logstash/inputs/amqp.rb +48 -0
- data/lib/logstash/inputs/base.rb +32 -0
- data/lib/logstash/inputs/file.rb +47 -0
- data/lib/logstash/inputs/syslog.rb +123 -0
- data/lib/logstash/inputs/tcp.rb +51 -0
- data/lib/logstash/logging.rb +82 -0
- data/lib/logstash/namespace.rb +6 -0
- data/lib/logstash/outputs.rb +15 -0
- data/lib/logstash/outputs/amqp.rb +48 -0
- data/lib/logstash/outputs/base.rb +29 -0
- data/lib/logstash/outputs/elasticsearch.rb +71 -0
- data/lib/logstash/outputs/gelf.rb +35 -0
- data/lib/logstash/outputs/mongodb.rb +19 -0
- data/lib/logstash/outputs/stdout.rb +15 -0
- data/lib/logstash/outputs/websocket.rb +35 -0
- data/lib/logstash/time.rb +27 -0
- data/lib/logstash/web/lib/elasticsearch.rb +79 -0
- data/lib/logstash/web/public/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/lib/logstash/web/public/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/lib/logstash/web/public/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/lib/logstash/web/public/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/lib/logstash/web/public/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/lib/logstash/web/public/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/lib/logstash/web/public/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/lib/logstash/web/public/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/lib/logstash/web/public/css/smoothness/images/ui-icons_222222_256x240.png +0 -0
- data/lib/logstash/web/public/css/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
- data/lib/logstash/web/public/css/smoothness/images/ui-icons_454545_256x240.png +0 -0
- data/lib/logstash/web/public/css/smoothness/images/ui-icons_888888_256x240.png +0 -0
- data/lib/logstash/web/public/css/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/lib/logstash/web/public/css/smoothness/jquery-ui-1.8.5.custom.css +572 -0
- data/lib/logstash/web/public/js/flot/API.txt +1024 -0
- data/lib/logstash/web/public/js/flot/FAQ.txt +71 -0
- data/lib/logstash/web/public/js/flot/LICENSE.txt +22 -0
- data/lib/logstash/web/public/js/flot/Makefile +15 -0
- data/lib/logstash/web/public/js/flot/NEWS.txt +340 -0
- data/lib/logstash/web/public/js/flot/PLUGINS.txt +105 -0
- data/lib/logstash/web/public/js/flot/README.txt +81 -0
- data/lib/logstash/web/public/js/flot/examples/ajax.html +143 -0
- data/lib/logstash/web/public/js/flot/examples/annotating.html +75 -0
- data/lib/logstash/web/public/js/flot/examples/arrow-down.gif +0 -0
- data/lib/logstash/web/public/js/flot/examples/arrow-left.gif +0 -0
- data/lib/logstash/web/public/js/flot/examples/arrow-right.gif +0 -0
- data/lib/logstash/web/public/js/flot/examples/arrow-up.gif +0 -0
- data/lib/logstash/web/public/js/flot/examples/basic.html +38 -0
- data/lib/logstash/web/public/js/flot/examples/data-eu-gdp-growth-1.json +4 -0
- data/lib/logstash/web/public/js/flot/examples/data-eu-gdp-growth-2.json +4 -0
- data/lib/logstash/web/public/js/flot/examples/data-eu-gdp-growth-3.json +4 -0
- data/lib/logstash/web/public/js/flot/examples/data-eu-gdp-growth-4.json +4 -0
- data/lib/logstash/web/public/js/flot/examples/data-eu-gdp-growth-5.json +4 -0
- data/lib/logstash/web/public/js/flot/examples/data-eu-gdp-growth.json +4 -0
- data/lib/logstash/web/public/js/flot/examples/data-japan-gdp-growth.json +4 -0
- data/lib/logstash/web/public/js/flot/examples/data-usa-gdp-growth.json +4 -0
- data/lib/logstash/web/public/js/flot/examples/dual-axis.html +39 -0
- data/lib/logstash/web/public/js/flot/examples/graph-types.html +75 -0
- data/lib/logstash/web/public/js/flot/examples/hs-2004-27-a-large_web.jpg +0 -0
- data/lib/logstash/web/public/js/flot/examples/image.html +45 -0
- data/lib/logstash/web/public/js/flot/examples/index.html +43 -0
- data/lib/logstash/web/public/js/flot/examples/interacting.html +93 -0
- data/lib/logstash/web/public/js/flot/examples/layout.css +6 -0
- data/lib/logstash/web/public/js/flot/examples/navigate.html +118 -0
- data/lib/logstash/web/public/js/flot/examples/selection.html +114 -0
- data/lib/logstash/web/public/js/flot/examples/setting-options.html +65 -0
- data/lib/logstash/web/public/js/flot/examples/stacking.html +77 -0
- data/lib/logstash/web/public/js/flot/examples/thresholding.html +54 -0
- data/lib/logstash/web/public/js/flot/examples/time.html +71 -0
- data/lib/logstash/web/public/js/flot/examples/tracking.html +95 -0
- data/lib/logstash/web/public/js/flot/examples/turning-series.html +98 -0
- data/lib/logstash/web/public/js/flot/examples/visitors.html +90 -0
- data/lib/logstash/web/public/js/flot/examples/zooming.html +98 -0
- data/lib/logstash/web/public/js/flot/excanvas.js +1427 -0
- data/lib/logstash/web/public/js/flot/excanvas.min.js +1 -0
- data/lib/logstash/web/public/js/flot/jquery.colorhelpers.js +174 -0
- data/lib/logstash/web/public/js/flot/jquery.colorhelpers.min.js +1 -0
- data/lib/logstash/web/public/js/flot/jquery.flot.crosshair.js +156 -0
- data/lib/logstash/web/public/js/flot/jquery.flot.crosshair.min.js +1 -0
- data/lib/logstash/web/public/js/flot/jquery.flot.image.js +237 -0
- data/lib/logstash/web/public/js/flot/jquery.flot.image.min.js +1 -0
- data/lib/logstash/web/public/js/flot/jquery.flot.js +2119 -0
- data/lib/logstash/web/public/js/flot/jquery.flot.min.js +1 -0
- data/lib/logstash/web/public/js/flot/jquery.flot.navigate.js +272 -0
- data/lib/logstash/web/public/js/flot/jquery.flot.navigate.min.js +1 -0
- data/lib/logstash/web/public/js/flot/jquery.flot.selection.js +299 -0
- data/lib/logstash/web/public/js/flot/jquery.flot.selection.min.js +1 -0
- data/lib/logstash/web/public/js/flot/jquery.flot.stack.js +152 -0
- data/lib/logstash/web/public/js/flot/jquery.flot.stack.min.js +1 -0
- data/lib/logstash/web/public/js/flot/jquery.flot.threshold.js +103 -0
- data/lib/logstash/web/public/js/flot/jquery.flot.threshold.min.js +1 -0
- data/lib/logstash/web/public/js/flot/jquery.js +4376 -0
- data/lib/logstash/web/public/js/flot/jquery.min.js +19 -0
- data/lib/logstash/web/public/js/jquery-hashchange-1.0.0.js +121 -0
- data/lib/logstash/web/public/js/jquery.livequery.js +250 -0
- data/lib/logstash/web/public/js/jquery.tmpl.min.js +1 -0
- data/lib/logstash/web/public/js/logstash.js +202 -0
- data/lib/logstash/web/server.rb +90 -0
- data/lib/logstash/web/views/header.haml +8 -0
- data/lib/logstash/web/views/layout.haml +21 -0
- data/lib/logstash/web/views/main/index.haml +5 -0
- data/lib/logstash/web/views/search/ajax.haml +32 -0
- data/lib/logstash/web/views/search/results.haml +17 -0
- data/lib/logstash/web/views/style.sass +50 -0
- data/patterns/firewalls +2 -0
- data/patterns/grok-patterns +90 -0
- data/patterns/haproxy +5 -0
- data/patterns/linux-syslog +7 -0
- data/patterns/nagios +7 -0
- data/patterns/ruby +2 -0
- 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
|