logstash-lite 0.2.20101118134500
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.
- 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
|