Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LOGSTASH-691: move amqp plugin to be an external plugin #338

Merged
merged 32 commits into from Feb 12, 2013
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
3e1a3a5
Copy amqp output plugin into rabbitmq file
zaccari Jan 25, 2013
dc3347a
Change AMQP to RabbitMQ names
zaccari Jan 25, 2013
9de2349
Remove 'name' field
zaccari Jan 25, 2013
573a05b
Remove amqp output plugin
zaccari Jan 25, 2013
e60fda4
Rename amqp input plugin to rabbitmq
zaccari Jan 25, 2013
6f1d69b
Change amqp -> rabbitmq for variable and documentation
zaccari Jan 29, 2013
793ec8b
Remove 'name' variable for rabbitmq input
zaccari Jan 29, 2013
cf24b4b
Rename amqp -> rabbitmq for input config variables
zaccari Jan 29, 2013
5ff3b7b
Minor rename amqp -> rabbitmq for agent doc
zaccari Jan 29, 2013
17336a9
Minor rename amqp -> rabbitmq for file config doc
zaccari Jan 29, 2013
608219e
Change amqp input test to rabbitmq
zaccari Jan 29, 2013
7482f3f
Minor doc change for rabbitmq output plugin
zaccari Jan 29, 2013
1fb6574
Minor doc fix for gemfire input plugin
zaccari Jan 29, 2013
233ea03
Update zenoss input plugin to use rabbitmq
zaccari Jan 29, 2013
e23b036
Update elasticsearch-river output plugin to use rabbitmq
zaccari Jan 29, 2013
ec79b59
Update amqp tutorial to use rabbitmq
zaccari Jan 29, 2013
371dce1
Update link to rabbitmq tutorial
zaccari Jan 29, 2013
ca3514c
Update minor reference to amqp to rabbitmq
zaccari Jan 29, 2013
24588d7
Change amqp -> rabbitmq in lgtm conf
zaccari Jan 29, 2013
0765674
Update elasticsearch river conf example to use rabbitmq
zaccari Jan 29, 2013
9773daa
Update indexer example to user rabbitmq
zaccari Jan 29, 2013
140669d
Merge branch 'LOGSTASH-691'
zaccari Jan 29, 2013
94b5ba4
Update rabbitmq input/output plugins with version info
zaccari Jan 29, 2013
c92ae36
Update gemspec to use tested version of bunny gem
zaccari Jan 29, 2013
4867a89
Track latest bunny gem
zaccari Jan 31, 2013
b40821b
Add frame_max variable to input/output rabbitmq plugins
zaccari Jan 31, 2013
0b9a27d
Update bunny version info for plugins
zaccari Jan 31, 2013
215f81a
Update info for arguments in the input plugin for rabbitmq
zaccari Jan 31, 2013
5c9d009
Add elastic-search ordered variable
zaccari Jan 31, 2013
3971be2
Remove minor trailing comma
zaccari Jan 31, 2013
1a02aa1
Add elasticsearch river slug for hostname and queue
zaccari Jan 31, 2013
6735ee4
Use right queue variable
zaccari Jan 31, 2013
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/index.html.erb
Expand Up @@ -20,7 +20,7 @@ layout: content_right
<li> <a href="tutorials/getting-started-centralized"> getting started (centralized) </a> </li>
<li> <a href="tutorials/10-minute-walkthrough"> 10-minute walkthrough</a> - a simple walkthrough to show you how to configure the logstash agent to process events and even old logs. </li>
<li> <a href="tutorials/metrics-from-logs"> Gathering metrics from logs </a> - take metrics from logs and ship them to graphite, ganglia, and more. </li>
<li> <a href="tutorials/just-enough-amqp-for-logstash">Just enough AMQP for Logstash </a> - Get a quick primer on AMQP and how to use it in Logstash! </li>
<li> <a href="tutorials/just-enough-rabbitmq-for-logstash">Just enough RabbitMQ for Logstash </a> - Get a quick primer on RabbitMQ and how to use it in Logstash! </li>
</ul>

<h3> plugin documentation </h3>
Expand Down
2 changes: 1 addition & 1 deletion docs/logging-tool-comparisons.md
Expand Up @@ -22,7 +22,7 @@ It provides you a simple event pipeline for taking events and logs from any
input, manipulating them with filters, and sending them to any output. Inputs
can be files, network, message brokers, etc. Filters are date and string
parsers, grep-like, etc. Outputs are data stores (elasticsearch, mongodb, etc),
message systems (amqp, stomp, etc), network (tcp, syslog), etc.
message systems (rabbitmq, stomp, etc), network (tcp, syslog), etc.

It also provides a web interface for doing search and analytics on your
logs.
Expand Down
@@ -1,10 +1,10 @@
---
title: Just Enough AMQP - logstash
title: Just Enough RabbitMQ - logstash
layout: content_right
---

While configuring your AMQP broker is out of scope for logstash, it's important
to understand how logstash uses AMQP. To do that, we need to understand a
While configuring your RabbitMQ broker is out of scope for logstash, it's important
to understand how logstash uses RabbitMQ. To do that, we need to understand a
little about AMQP.

You should also consider reading
Expand Down Expand Up @@ -35,9 +35,8 @@ routing key. Routing keys are discussed below.

## Broker

A broker is simply the AMQP server software. There are several brokers but the
most common (and arguably popular) is [RabbitMQ](http://www.rabbitmq.com).
Some others are Apache Qpid (and the commercial version - RedHat MRG)
A broker is simply the AMQP server software. There are several brokers, but this
tutorial will cover the most common (and arguably popular), [RabbitMQ](http://www.rabbitmq.com).

# Routing Keys

Expand Down Expand Up @@ -112,19 +111,19 @@ support routing keys. Topic exchanges do support them. Just like a fanout
exchange, all bound queues see all messages with the additional filter of the
routing key.

# AMQP in logstash
# RabbitMQ in logstash

As stated earlier, in Logstash, Outputs publish to Exchanges. Inputs read from
Queues that are bound to Exchanges. Logstash uses the `bunny` AMQP library for
Queues that are bound to Exchanges. Logstash uses the `bunny` RabbitMQ library for
interaction with a broker. Logstash endeavors to expose as much of the
configuration for both exchanges and queues. There are many different tunables
that you might be concerned with setting - including things like message
durability or persistence of declared queues/exchanges. See the relevant input
and output documentation for AMQP for a full list of tunables.
and output documentation for RabbitMQ for a full list of tunables.

# Sample configurations, tips, tricks and gotchas

There are several examples in the logstash source directory of AMQP usage,
There are several examples in the logstash source directory of RabbitMQ usage,
however a few general rules might help eliminate any issues.

## Check your bindings
Expand All @@ -136,19 +135,19 @@ sender agent

input { stdin { type = "test" } }
output {
amqp {
name => "test_exchange"
host => "my_amqp_server"
rabbitmq {
exchange => "test_exchange"
host => "my_rabbitmq_server"
exchange_type => "fanout"
}
}

receiver agent

input {
amqp {
name => "test_queue"
host => "my_amqp_server"
rabbitmq {
queue => "test_queue"
host => "my_rabbitmq_server"
exchange => "test_exchange" # This matches the exchange declared above
}
}
Expand All @@ -157,15 +156,15 @@ receiver agent
## Message persistence

By default, logstash will attempt to ensure that you don't lose any messages.
This is reflected in the AMQP default settings as well. However there are
cases where you might not want this. A good example is where AMQP is not your
This is reflected in the RabbitMQ default settings as well. However there are
cases where you might not want this. A good example is where RabbitMQ is not your
primary method of shipping.

In the following example, we use AMQP as a sniffing interface. Our primary
destination is the embedded ElasticSearch instance. We have a secondary AMQP
In the following example, we use RabbitMQ as a sniffing interface. Our primary
destination is the embedded ElasticSearch instance. We have a secondary RabbitMQ
output that we use for duplicating messages. However we disable persistence and
durability on this interface so that messages don't pile up waiting for
delivery. We only use AMQP when we want to watch messages in realtime.
delivery. We only use RabbitMQ when we want to watch messages in realtime.
Additionally, we're going to leverage routing keys so that we can optionally
filter incoming messages to subsets of hosts. The exercise of getting messages
to this logstash agent are left up to the user.
Expand All @@ -176,9 +175,9 @@ to this logstash agent are left up to the user.

output {
elasticsearch { embedded => true }
amqp {
name => "logtail"
host => "my_amqp_server"
rabbitmq {
exchange => "logtail"
host => "my_rabbitmq_server"
exchange_type => "topic" # We use topic here to enable pub/sub with routing keys
key => "logs.%{host}"
durable => false # If rabbitmq restarts, the exchange disappears.
Expand Down
10 changes: 5 additions & 5 deletions etc/agent.lgtm.conf
Expand Up @@ -17,17 +17,17 @@ input {

output {
# This will be your durable shipping mechanism
amqp {
host => "myamqpserver"
rabbitmq {
host => "myrabbitmqserver"
exchange_type => "fanout"
name => "rawlogs"
exchange => "rawlogs"
}
# This is an optional non-durable shipping mechanism
# With this, you can sniff logs from your own code
amqp {
rabbitmq {
host => "127.0.0.1"
exchange_type => "topic"
name => "logsniff"
exchange => "logsniff"
durable => false
persistent => false
# The following is optional
Expand Down
2 changes: 1 addition & 1 deletion etc/examples/esriver.conf
Expand Up @@ -16,6 +16,6 @@ output {
stdout { }
elasticsearch_river {
es_host => "localhost"
amqp_host => "localhost"
rabbitmq_host => "localhost"
}
}
4 changes: 2 additions & 2 deletions etc/examples/indexer.conf
@@ -1,10 +1,10 @@
input {
amqp {
rabbitmq {
host => "127.0.0.1"
user => "guest"
pass => "guest"
exchange => "logstash"
name => "testing"
queue => "testing"
type => "all"
}

Expand Down
4 changes: 2 additions & 2 deletions lib/logstash/agent.rb
Expand Up @@ -152,8 +152,8 @@ def parse_options(args)
# Load any plugins that we have flags for.
# TODO(sissel): The --<plugin> flag support currently will load
# any matching plugins input, output, or filter. This means, for example,
# that the 'amqp' input *and* output plugin will be loaded if you pass
# --amqp-foo flag. This might cause confusion, but it seems reasonable for
# that the 'rabbitmq' input *and* output plugin will be loaded if you pass
# --rabbitmq-foo flag. This might cause confusion, but it seems reasonable for
# now that any same-named component will have the same flags.
plugins = []
args.each do |arg|
Expand Down
4 changes: 2 additions & 2 deletions lib/logstash/config/file.rb
Expand Up @@ -43,7 +43,7 @@ def parse
tryload o[:type], :base
type = registry[o[:type]]

# Load the plugin itself (inputs/file, outputs/amqp, etc)
# Load the plugin itself (inputs/file, outputs/rabbitmq, etc)
# TODO(sissel): Error handling
tryload o[:type], o[:plugin]
plugin = registry[o[:plugin]]
Expand Down Expand Up @@ -84,7 +84,7 @@ def each(&block)
@config.each do |type, plugin_config_array|
# plugin_config_array has arrays of each component config:
# input {
# amqp { ... }
# rabbitmq { ... }
# file { ... }
# file { ... }
# }
Expand Down
2 changes: 1 addition & 1 deletion lib/logstash/config/test.conf
@@ -1,5 +1,5 @@
input {
amqp {
rabbitmq {
port => 12345
tag => [ a, b, c ]
}
Expand Down
2 changes: 1 addition & 1 deletion lib/logstash/inputs/gemfire.rb
Expand Up @@ -234,4 +234,4 @@ def afterRegionDestroy(event)
def afterRegionInvalidate(event)
@logger.debug("afterRegionInvalidate #{event}")
end
end # class LogStash::Inputs::Amqp
end # class LogStash::Inputs::Gemfire
77 changes: 33 additions & 44 deletions lib/logstash/inputs/amqp.rb → lib/logstash/inputs/rabbitmq.rb
Expand Up @@ -2,44 +2,40 @@
require "logstash/namespace"
require "cgi" # for CGI.escape

# Pull events from an AMQP exchange.
#
# <b> NOTE: THIS IS ONLY KNOWN TO WORK WITH RECENT RELEASES OF RABBITMQ. Any
# other amqp broker will not work with this plugin. I do not know why. If you
# need support for brokers other than rabbitmq, please file bugs here:
# <https://github.com/ruby-amqp/bunny> </b>
# Pull events from a RabbitMQ exchange.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you have a recommended version, can you specify it? Many distros ship with ancient versions of rabbitmq that probably don't work anymore with the bunny gem.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the input/output plugins with version info for Bunny & RabbitMQ, and also added version requirements to the gemspec for Bunny 0.8.x. Let me know if any more information would help.

#
# The default settings will create an entirely transient queue and listen for all messages by default.
# If you need durability or any other advanced settings, please set the appropriate options
class LogStash::Inputs::Amqp < LogStash::Inputs::Threadable
#
# This has been tested with Bunny 0.8.x, which supports RabbitMQ 2.x and 3.x. You can
# find links to both here:
#
# * RabbitMQ - <http://www.rabbitmq.com/>
# * Bunny - <https://github.com/ruby-amqp/bunny>
class LogStash::Inputs::RabbitMQ < LogStash::Inputs::Threadable

config_name "amqp"
plugin_status "unsupported"
config_name "rabbitmq"
plugin_status "beta"

# Your amqp broker's custom arguments. For mirrored queues in RabbitMQ: [ "x-ha-policy", "all" ]
# Custom arguments. For mirrored queues in rabbitmq: [ "x-ha-policy", "all" ]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With RabbitMQ 3x, we do not need any more to user x-ha-policy when declaring then queue.
Mirroring is done by policy, so the customs arguments are not needed for HA.
See http://www.rabbitmq.com/blog/2012/11/19/breaking-things-with-rabbitmq-3-0/

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the custom arguments variable no longer needed (and should be removed), or should just the doc example with policy be removed?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assumes rabbitmq 3. Ubuntu 12.04 still ships RabbitMQ 2.7, so I expect there to continue to be ubuntu users on 2.x at least another 2 years and debian for another 4-6 years.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good point.

So it could be good to have both configuration extract for RabbitMQ 2x and
3x.

config :arguments, :validate => :array, :default => []

# Your amqp server address
# Your rabbitmq server address
config :host, :validate => :string, :required => true
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to have host failover. If the main host is not reachable, we could try with the following members of the RabbitMQ cluster.
I do not know if the bunny client can take a list of hosts and make the failover or if the failover has to be done manually in the plugin.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order to preserve some compatibility, should we just add a new variable, 'hosts', that takes an array of hosts that tries to connect to each? Should this fall back to the normal "host" variable if all hosts fail?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make sense for me.
I make it work temporarily by incrementing a integer and trying to connect to the next arrayofhosts[integer].

Probablt, bunny has to proper parameter to handle it natively, like java amqp client.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like logstash already handles the multiple hosts field, like this for redis: https://github.com/logstash/logstash/blob/master/lib/logstash/outputs/redis.rb#L21-L25. I will look into this.


# The AMQP port to connect on
# The rabbitmq port to connect on
config :port, :validate => :number, :default => 5672

# Your amqp username
# Your rabbitmq username
config :user, :validate => :string, :default => "guest"

# Your amqp password
# Your rabbitmq password
config :password, :validate => :password, :default => "guest"

# The name of the queue. Depricated due to conflicts with puppet naming convention.
# Replaced by 'queue' variable. See LOGSTASH-755
config :name, :validate => :string, :deprecated => true

# The name of the queue.
config :queue, :validate => :string, :default => ""

# The name of the exchange to bind the queue. This is analogous to the 'amqp
# output' [config 'name'](../outputs/amqp)
# The name of the exchange to bind the queue.
config :exchange, :validate => :string, :required => true

# The routing key to use. This is only valid for direct or fanout exchanges
Expand Down Expand Up @@ -90,43 +86,36 @@ def initialize(params)
end # def initialize

public
def register

if @name
if @queue
@logger.error("'name' and 'queue' are the same setting, but 'name' is deprecated. Please use only 'queue'")
end
@queue = @name
end
def register

@logger.info("Registering input #{@url}")
require "bunny" # rubygem 'bunny'
@vhost ||= "/"
@port ||= 5672
@key ||= "#"
@amqpsettings = {
@rabbitmq_settings = {
:vhost => @vhost,
:host => @host,
:port => @port,
}
@amqpsettings[:user] = @user if @user
@amqpsettings[:pass] = @password.value if @password
@amqpsettings[:logging] = @debug
@amqpsettings[:ssl] = @ssl if @ssl
@amqpsettings[:verify_ssl] = @verify_ssl if @verify_ssl
@amqpurl = "amqp://"
@rabbitmq_settings[:user] = @user if @user
@rabbitmq_settings[:pass] = @password.value if @password
@rabbitmq_settings[:logging] = @debug
@rabbitmq_settings[:ssl] = @ssl if @ssl
@rabbitmq_settings[:verify_ssl] = @verify_ssl if @verify_ssl
@rabbitmq_url = "amqp://"
if @user
@amqpurl << @user if @user
@amqpurl << ":#{CGI.escape(@password.to_s)}" if @password
@amqpurl << "@"
@rabbitmq_url << @user if @user
@rabbitmq_url << ":#{CGI.escape(@password.to_s)}" if @password
@rabbitmq_url << "@"
end
@amqpurl += "#{@host}:#{@port}#{@vhost}/#{@queue}"
@rabbitmq_url += "#{@host}:#{@port}#{@vhost}/#{@queue}"
end # def register

def run(queue)
begin
@logger.debug("Connecting with AMQP settings #{@amqpsettings.inspect} to set up queue #{@queue.inspect}")
@bunny = Bunny.new(@amqpsettings)
@logger.debug("Connecting with RabbitMQ settings #{@rabbitmq_settings.inspect} to set up queue #{@queue.inspect}")
@bunny = Bunny.new(@rabbitmq_settings)
return if terminating?
@bunny.start
@bunny.qos({:prefetch_count => @prefetch_count})
Expand All @@ -137,14 +126,14 @@ def run(queue)
@bunnyqueue.bind(@exchange, :key => @key)

@bunnyqueue.subscribe({:ack => @ack}) do |data|
e = to_event(data[:payload], @amqpurl)
e = to_event(data[:payload], @rabbitmq_url)
if e
queue << e
end
end # @bunnyqueue.subscribe

rescue *[Bunny::ConnectionError, Bunny::ServerDownError] => e
@logger.error("AMQP connection error, will reconnect: #{e}")
@logger.error("RabbitMQ connection error, will reconnect: #{e}")
# Sleep for a bit before retrying.
# TODO(sissel): Write 'backoff' method?
sleep(1)
Expand All @@ -158,4 +147,4 @@ def teardown
@bunny.close if @bunny
finished
end # def teardown
end # class LogStash::Inputs::Amqp
end # class LogStash::Inputs::RabbitMQ