fluent-plugin-zmq-pub 0.0.2 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +37 -6
- data/fluent-plugin-zmq-pub.gemspec +5 -5
- data/lib/fluent/plugin/in_zmq_sub.rb +87 -0
- data/lib/fluent/plugin/out_zmq_pub.rb +24 -7
- data/test/helper.rb +1 -0
- data/test/plugin/test_in_zmq_sub.rb +97 -0
- data/test/plugin/test_out_zeromq_pub.rb +42 -3
- metadata +9 -6
data/README.rdoc
CHANGED
@@ -8,6 +8,18 @@ Fluentd plugin to publish records to ZeroMQ.
|
|
8
8
|
|
9
9
|
Sometimes I wanted to 'sniff' fluentd stream -- running my own programs to the stream without changing fluentd configuration and restarting fluentd. With this plugin, fluentd records are always published to ZeroMQ regardless of the existance of subscriber. After that I can start and stop my subcriber programs at any time.
|
10
10
|
|
11
|
+
== Dependence
|
12
|
+
|
13
|
+
This plugin use ffi-rzmq to support ZMQ, and need v3.2 or greater version of ZMQ library installed in your system.
|
14
|
+
|
15
|
+
== Installation
|
16
|
+
|
17
|
+
You need to install ZeroMQ libraries before installing this plugin
|
18
|
+
|
19
|
+
## (RedHat/CentOS)
|
20
|
+
# yum install zeromq3 zeromq3-devel
|
21
|
+
# fluent-gem install fluent-plugin-zmq-pub
|
22
|
+
|
11
23
|
== Configuration
|
12
24
|
|
13
25
|
<match zmq.**>
|
@@ -15,6 +27,7 @@ Sometimes I wanted to 'sniff' fluentd stream -- running my own programs to the s
|
|
15
27
|
pubkey ${tag}:${key1}
|
16
28
|
bindaddr tcp://*:5556
|
17
29
|
flush_interval 1s
|
30
|
+
bulk_send true
|
18
31
|
</match>
|
19
32
|
|
20
33
|
* 'pubkey' specifies the publish key to ZeroMQ.
|
@@ -22,6 +35,7 @@ Sometimes I wanted to 'sniff' fluentd stream -- running my own programs to the s
|
|
22
35
|
* Actual record to be published is '<pubkey> <reocord.to_msgpack>'.
|
23
36
|
* Subscriber can subscribe by '<pubkey>'.
|
24
37
|
* 'bindaddr' is the address to which ZeroMQ publisher socket to be bound.
|
38
|
+
* If 'bulk_send' is set to true, send multiple records with the same publish key in one 'send_string' method. This improves the performance.
|
25
39
|
|
26
40
|
== Example usage
|
27
41
|
|
@@ -29,7 +43,7 @@ Put the configuration above to fluentd.conf, and save this sample code as 'sampl
|
|
29
43
|
|
30
44
|
#!/usr/bin/env ruby
|
31
45
|
|
32
|
-
require '
|
46
|
+
require 'ffi-rzmq'
|
33
47
|
require 'msgpack'
|
34
48
|
|
35
49
|
context = ZMQ::Context.new(1)
|
@@ -45,11 +59,13 @@ Put the configuration above to fluentd.conf, and save this sample code as 'sampl
|
|
45
59
|
end
|
46
60
|
|
47
61
|
while true
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
puts "
|
52
|
-
puts "
|
62
|
+
msg = ''
|
63
|
+
while subscriber.recv_string(msg,ZMQ::DONTWAIT) && msg.size > 0
|
64
|
+
record = MessagePack.unpack(msg.split(" ",2)[1])
|
65
|
+
puts "tag: #{record[0]}"
|
66
|
+
puts "time: #{record[1]}"
|
67
|
+
puts "record: #{record[2]}"
|
68
|
+
msg = ''
|
53
69
|
end
|
54
70
|
sleep(0.1)
|
55
71
|
end
|
@@ -73,6 +89,21 @@ Then you will get the following output from sample_sub.rb
|
|
73
89
|
|
74
90
|
The nice thing is that once you put this plugin to your fluentd.conf and start fluentd, you can start and stop any subscriber programs without changing fluentd configuration.
|
75
91
|
|
92
|
+
== zmq_sub input plugin
|
93
|
+
|
94
|
+
Input plugin to subscribe the output of zmq_pub is also included. Here is the example configuration.
|
95
|
+
|
96
|
+
<source>
|
97
|
+
type zmq_sub
|
98
|
+
publisher tcp://127.0.0.1:5556
|
99
|
+
bulk_send true
|
100
|
+
subkey zmq.,zmq2
|
101
|
+
</source>
|
102
|
+
|
103
|
+
* If zmq_pub set `bulk_send` to true, zmq_sub also set it to true.
|
104
|
+
* `subkey` is a comma separated list of keys to subscribe. In this example, keys starting "zmq." or "zmq2." will be subscribed.
|
105
|
+
|
106
|
+
|
76
107
|
== Copyright
|
77
108
|
|
78
109
|
* Copyright (c) 2013- OGIBAYASHI Hironori (@angostura11)
|
@@ -4,8 +4,8 @@ $:.push File.expand_path('../lib', __FILE__)
|
|
4
4
|
Gem::Specification.new do |gem|
|
5
5
|
gem.authors = ["OGIBAYASHI Hironori"]
|
6
6
|
gem.email = ["ogibayashi@gmail.com"]
|
7
|
-
gem.description = %q{0MQ publisher plugin for fluentd}
|
8
|
-
gem.summary = %q{0MQ publisher plugin for fluentd}
|
7
|
+
gem.description = %q{0MQ publisher/subscriber plugin for fluentd}
|
8
|
+
gem.summary = %q{0MQ publisher/subscriber plugin for fluentd, use 0MQ v3.2 or greater version}
|
9
9
|
gem.homepage = ""
|
10
10
|
gem.licenses = ["Apache License, Version 2.0"]
|
11
11
|
|
@@ -14,9 +14,9 @@ Gem::Specification.new do |gem|
|
|
14
14
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
15
15
|
gem.name = "fluent-plugin-zmq-pub"
|
16
16
|
gem.require_paths = ["lib"]
|
17
|
-
gem.version = "0.0.
|
17
|
+
gem.version = "0.0.4"
|
18
18
|
gem.add_development_dependency "fluentd"
|
19
19
|
gem.add_runtime_dependency "fluentd"
|
20
|
-
gem.add_development_dependency "
|
21
|
-
gem.add_runtime_dependency "
|
20
|
+
gem.add_development_dependency "ffi-rzmq"
|
21
|
+
gem.add_runtime_dependency "ffi-rzmq"
|
22
22
|
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Fluent
|
2
|
+
|
3
|
+
class ZmqSubInput < Fluent::Input
|
4
|
+
Fluent::Plugin.register_input('zmq_sub', self)
|
5
|
+
|
6
|
+
config_param :subkey, :string, :default => ""
|
7
|
+
config_param :publisher, :string, :default => "tcp://127.0.0.1:5556"
|
8
|
+
config_param :bulk_send, :bool, :default => false
|
9
|
+
|
10
|
+
attr_reader :subkeys
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
super
|
14
|
+
require 'ffi-rzmq'
|
15
|
+
end
|
16
|
+
|
17
|
+
def configure(conf)
|
18
|
+
super
|
19
|
+
@subkeys = @subkey.split(",")
|
20
|
+
end
|
21
|
+
|
22
|
+
def start
|
23
|
+
super
|
24
|
+
@context =ZMQ::Context.new()
|
25
|
+
@thread = Thread.new(&method(:run))
|
26
|
+
end
|
27
|
+
|
28
|
+
def shutdown
|
29
|
+
super
|
30
|
+
Thread.kill(@thread)
|
31
|
+
@thread.join
|
32
|
+
@context.terminate
|
33
|
+
end
|
34
|
+
|
35
|
+
def run
|
36
|
+
begin
|
37
|
+
@subscriber = @context.socket(ZMQ::SUB)
|
38
|
+
@subscriber.connect(@publisher)
|
39
|
+
if @subkeys.size > 0
|
40
|
+
@subkeys.each do |k|
|
41
|
+
@subscriber.setsockopt(ZMQ::SUBSCRIBE,k)
|
42
|
+
end
|
43
|
+
else
|
44
|
+
@subscriber.setsockopt(ZMQ::SUBSCRIBE,'')
|
45
|
+
end
|
46
|
+
loop do
|
47
|
+
msg = ''
|
48
|
+
while @subscriber.recv_string(msg,ZMQ::DONTWAIT) && msg.size > 0
|
49
|
+
begin
|
50
|
+
(key, records) = msg.split(" ",2)
|
51
|
+
records = MessagePack.unpack(records)
|
52
|
+
if @bulk_send && records[0].class == Array
|
53
|
+
es = MultiEventStream.new
|
54
|
+
prev_tag = nil
|
55
|
+
records.each do |tag, time, record|
|
56
|
+
if prev_tag && prev_tag != tag
|
57
|
+
Engine.emit_stream(prev_tag, es)
|
58
|
+
es = MultiEventStream.new
|
59
|
+
end
|
60
|
+
es.add(time, record)
|
61
|
+
prev_tag = tag
|
62
|
+
end
|
63
|
+
Engine.emit_stream(prev_tag, es) if es.to_a.size > 0
|
64
|
+
else
|
65
|
+
Engine.emit(*records)
|
66
|
+
end
|
67
|
+
rescue => e
|
68
|
+
log.warn "Error in processing message.",:error_class => e.class, :error => e
|
69
|
+
log.warn_backtrace
|
70
|
+
end
|
71
|
+
msg = ''
|
72
|
+
end
|
73
|
+
sleep(0.1)
|
74
|
+
end
|
75
|
+
rescue => e
|
76
|
+
log.error "error occurred while executing plugin.", :error_class => e.class, :error => e
|
77
|
+
log.warn_backtrace
|
78
|
+
ensure
|
79
|
+
if @subscriber
|
80
|
+
@subscriber.close
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
@@ -4,10 +4,13 @@ module Fluent
|
|
4
4
|
|
5
5
|
config_param :pubkey, :string
|
6
6
|
config_param :bindaddr, :string, :default => 'tcp://*:5556'
|
7
|
+
config_param :highwatermark, :integer, :default => 1000
|
8
|
+
# Send multiple record with the same publish key at once
|
9
|
+
config_param :bulk_send, :bool, :default => false
|
7
10
|
|
8
11
|
def initialize
|
9
12
|
super
|
10
|
-
require '
|
13
|
+
require 'ffi-rzmq'
|
11
14
|
@mutex = Mutex.new
|
12
15
|
end
|
13
16
|
|
@@ -17,8 +20,9 @@ module Fluent
|
|
17
20
|
|
18
21
|
def start
|
19
22
|
super
|
20
|
-
@context = ZMQ::Context.new(
|
23
|
+
@context = ZMQ::Context.new()
|
21
24
|
@publisher = @context.socket(ZMQ::PUB)
|
25
|
+
@publisher.setsockopt(ZMQ::SNDHWM, @highwatermark)
|
22
26
|
@publisher.bind(@bindaddr)
|
23
27
|
end
|
24
28
|
|
@@ -27,6 +31,8 @@ module Fluent
|
|
27
31
|
end
|
28
32
|
|
29
33
|
def write(chunk)
|
34
|
+
records = { }
|
35
|
+
# to_msgpack in format, unpack in write, then to_msgpack again... better way?
|
30
36
|
chunk.msgpack_each{ |record|
|
31
37
|
pubkey_replaced = @pubkey.gsub(/\${(.*?)}/){ |s|
|
32
38
|
case $1
|
@@ -36,18 +42,29 @@ module Fluent
|
|
36
42
|
record[2][$1]
|
37
43
|
end
|
38
44
|
}
|
39
|
-
|
40
|
-
|
45
|
+
if @bulk_send
|
46
|
+
records[pubkey_replaced] ||= []
|
47
|
+
records[pubkey_replaced] << record
|
48
|
+
else
|
49
|
+
@mutex.synchronize {
|
50
|
+
@publisher.send_string(pubkey_replaced + " " + record.to_msgpack,ZMQ::DONTWAIT)
|
51
|
+
}
|
52
|
+
end
|
53
|
+
}
|
54
|
+
if @bulk_send
|
41
55
|
@mutex.synchronize {
|
42
|
-
|
56
|
+
records.each{ |k,v|
|
57
|
+
@publisher.send_string(k + " " + v.to_msgpack,ZMQ::DONTWAIT)
|
58
|
+
}
|
43
59
|
}
|
44
|
-
|
60
|
+
end
|
61
|
+
|
45
62
|
end
|
46
63
|
|
47
64
|
def shutdown
|
48
65
|
super
|
49
66
|
@publisher.close
|
50
|
-
@context.
|
67
|
+
@context.terminate
|
51
68
|
end
|
52
69
|
|
53
70
|
end
|
data/test/helper.rb
CHANGED
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'ffi-rzmq'
|
3
|
+
|
4
|
+
class ZmqSubIutputTest < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
Fluent::Test.setup
|
7
|
+
@context = ZMQ::Context.new()
|
8
|
+
@publisher = @context.socket(ZMQ::PUB)
|
9
|
+
@publisher.bind("tcp://*:5556")
|
10
|
+
end
|
11
|
+
|
12
|
+
def teardown
|
13
|
+
@publisher.close
|
14
|
+
@context.terminate
|
15
|
+
end
|
16
|
+
|
17
|
+
PUBLISHER = "tcp://127.0.0.1:5556"
|
18
|
+
CONFIG = %[
|
19
|
+
publisher #{PUBLISHER}
|
20
|
+
subkey test1.,test2.
|
21
|
+
]
|
22
|
+
|
23
|
+
|
24
|
+
def create_driver(conf=CONFIG)
|
25
|
+
Fluent::Test::InputTestDriver.new(Fluent::ZmqSubInput).configure(conf)
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_configure
|
29
|
+
d = create_driver(CONFIG + "bulk_send true")
|
30
|
+
assert_equal PUBLISHER, d.instance.publisher
|
31
|
+
assert_equal ["test1.","test2."], d.instance.subkeys
|
32
|
+
assert_equal true, d.instance.bulk_send
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_receive
|
36
|
+
d = create_driver
|
37
|
+
|
38
|
+
time = Time.parse("2011-01-02 13:14:15 UTC").to_i
|
39
|
+
Fluent::Engine.now = time
|
40
|
+
|
41
|
+
d.expect_emit "test1.aa", time, {"a"=>1}
|
42
|
+
d.expect_emit "test2.bb", time, {"a"=>2}
|
43
|
+
|
44
|
+
d.run do
|
45
|
+
d.expected_emits.each {|tag,time,record|
|
46
|
+
send_record("dummy",time,record) # This record should not be received.
|
47
|
+
send_record(tag,time,record)
|
48
|
+
}
|
49
|
+
sleep 1
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_no_subkey
|
54
|
+
d = create_driver("")
|
55
|
+
|
56
|
+
time = Time.parse("2011-01-02 13:14:15 UTC").to_i
|
57
|
+
Fluent::Engine.now = time
|
58
|
+
|
59
|
+
d.expect_emit "test1.aa", time, {"a"=>1}
|
60
|
+
d.expect_emit "test2.bb", time, {"a"=>2}
|
61
|
+
|
62
|
+
d.run do
|
63
|
+
d.expected_emits.each {|tag,time,record|
|
64
|
+
send_record(tag,time,record)
|
65
|
+
}
|
66
|
+
sleep 1
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
def test_receive_bulk
|
72
|
+
d = create_driver(CONFIG + "bulk_send true")
|
73
|
+
|
74
|
+
time = Time.parse("2011-01-02 13:14:15 UTC").to_i
|
75
|
+
Fluent::Engine.now = time
|
76
|
+
|
77
|
+
d.expect_emit "test3.aa", time, {"a"=>1}
|
78
|
+
d.expect_emit "test4.bb", time, {"a"=>2}
|
79
|
+
|
80
|
+
d.run do
|
81
|
+
record_to_send = []
|
82
|
+
d.expected_emits.each {|tag,time,record|
|
83
|
+
record_to_send << [tag,time,record]
|
84
|
+
}
|
85
|
+
send_record_bulk("test1.aa",record_to_send)
|
86
|
+
sleep 1
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def send_record(tag,time,record)
|
91
|
+
@publisher.send_string(tag + " " + [tag,time,record].to_msgpack)
|
92
|
+
end
|
93
|
+
|
94
|
+
def send_record_bulk(tag,records)
|
95
|
+
@publisher.send_string(tag + " " + records.to_msgpack)
|
96
|
+
end
|
97
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'helper'
|
2
|
-
require '
|
2
|
+
require 'ffi-rzmq'
|
3
3
|
|
4
4
|
class ZmqPubOutputTest < Test::Unit::TestCase
|
5
5
|
def setup
|
@@ -11,13 +11,24 @@ class ZmqPubOutputTest < Test::Unit::TestCase
|
|
11
11
|
|
12
12
|
def teardown
|
13
13
|
@subscriber.close
|
14
|
-
@context.
|
14
|
+
@context.terminate
|
15
15
|
end
|
16
16
|
|
17
17
|
CONFIG = %[
|
18
18
|
pubkey ${tag}:${key1}
|
19
19
|
bindaddr tcp://*:5556
|
20
20
|
]
|
21
|
+
|
22
|
+
CONFIG_BULK = %[
|
23
|
+
pubkey ${tag}:${key1}
|
24
|
+
bindaddr tcp://*:5556
|
25
|
+
bulk_send true
|
26
|
+
]
|
27
|
+
|
28
|
+
CONFIG_BY_TAG = %[
|
29
|
+
pubkey ${tag}
|
30
|
+
bindaddr tcp://*:5556
|
31
|
+
]
|
21
32
|
|
22
33
|
def create_driver(conf = CONFIG, tag='test')
|
23
34
|
Fluent::Test::BufferedOutputTestDriver.new(Fluent::ZmqPubOutput, tag).configure(conf)
|
@@ -50,16 +61,44 @@ class ZmqPubOutputTest < Test::Unit::TestCase
|
|
50
61
|
time = Time.parse("2011-01-02 13:14:15 UTC").to_i
|
51
62
|
d.emit({"key1"=>"aaa"}, time)
|
52
63
|
d.emit({"key1"=>"bbb", "key2"=>3}, time)
|
64
|
+
d.emit({"key1"=>"aaa", "key2"=>4}, time)
|
53
65
|
|
54
66
|
d.run
|
55
67
|
sleep 1
|
56
68
|
|
57
|
-
msg =
|
69
|
+
msg = ''
|
70
|
+
@subscriber.recv_string(msg,ZMQ::DONTWAIT)
|
58
71
|
(key, record) = msg.split(" ",2)
|
59
72
|
assert_equal ["test",time.to_i,{ "key1" => "aaa"}].to_msgpack, record
|
60
73
|
|
74
|
+
msg = ''
|
75
|
+
@subscriber.recv_string(msg,ZMQ::DONTWAIT)
|
76
|
+
(key, record) = msg.split(" ",2)
|
77
|
+
assert_equal ["test",time.to_i,{ "key1" => "aaa","key2" => 4 }].to_msgpack, record
|
78
|
+
|
61
79
|
end
|
62
80
|
|
81
|
+
def test_write_bulk
|
82
|
+
d = create_driver(CONFIG_BULK)
|
83
|
+
@subscriber.setsockopt(ZMQ::SUBSCRIBE,"test:aaa")
|
84
|
+
|
85
|
+
time = Time.parse("2011-01-02 13:14:15 UTC").to_i
|
86
|
+
d.emit({"key1"=>"aaa"}, time)
|
87
|
+
d.emit({"key1"=>"bbb", "key2"=>3}, time)
|
88
|
+
d.emit({"key1"=>"aaa", "key2"=>4}, time)
|
89
|
+
|
90
|
+
d.run
|
91
|
+
sleep 1
|
92
|
+
|
93
|
+
msg = ''
|
94
|
+
@subscriber.recv_string(msg,ZMQ::DONTWAIT)
|
95
|
+
(key, record) = msg.split(" ",2)
|
96
|
+
assert_equal [["test",time.to_i,{ "key1" => "aaa"}],
|
97
|
+
["test",time.to_i,{ "key1" => "aaa","key2" => 4 }]].to_msgpack, record
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
|
63
102
|
end
|
64
103
|
|
65
104
|
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: fluent-plugin-zmq-pub
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0.
|
5
|
+
version: 0.0.4
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- OGIBAYASHI Hironori
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2014-
|
13
|
+
date: 2014-10-09 00:00:00 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: fluentd
|
@@ -35,7 +35,7 @@ dependencies:
|
|
35
35
|
type: :runtime
|
36
36
|
version_requirements: *id002
|
37
37
|
- !ruby/object:Gem::Dependency
|
38
|
-
name:
|
38
|
+
name: ffi-rzmq
|
39
39
|
prerelease: false
|
40
40
|
requirement: &id003 !ruby/object:Gem::Requirement
|
41
41
|
none: false
|
@@ -46,7 +46,7 @@ dependencies:
|
|
46
46
|
type: :development
|
47
47
|
version_requirements: *id003
|
48
48
|
- !ruby/object:Gem::Dependency
|
49
|
-
name:
|
49
|
+
name: ffi-rzmq
|
50
50
|
prerelease: false
|
51
51
|
requirement: &id004 !ruby/object:Gem::Requirement
|
52
52
|
none: false
|
@@ -56,7 +56,7 @@ dependencies:
|
|
56
56
|
version: "0"
|
57
57
|
type: :runtime
|
58
58
|
version_requirements: *id004
|
59
|
-
description: 0MQ publisher plugin for fluentd
|
59
|
+
description: 0MQ publisher/subscriber plugin for fluentd
|
60
60
|
email:
|
61
61
|
- ogibayashi@gmail.com
|
62
62
|
executables: []
|
@@ -72,8 +72,10 @@ files:
|
|
72
72
|
- README.rdoc
|
73
73
|
- Rakefile
|
74
74
|
- fluent-plugin-zmq-pub.gemspec
|
75
|
+
- lib/fluent/plugin/in_zmq_sub.rb
|
75
76
|
- lib/fluent/plugin/out_zmq_pub.rb
|
76
77
|
- test/helper.rb
|
78
|
+
- test/plugin/test_in_zmq_sub.rb
|
77
79
|
- test/plugin/test_out_zeromq_pub.rb
|
78
80
|
homepage: ""
|
79
81
|
licenses:
|
@@ -101,7 +103,8 @@ rubyforge_project:
|
|
101
103
|
rubygems_version: 1.8.25
|
102
104
|
signing_key:
|
103
105
|
specification_version: 3
|
104
|
-
summary: 0MQ publisher plugin for fluentd
|
106
|
+
summary: 0MQ publisher/subscriber plugin for fluentd, use 0MQ v3.2 or greater version
|
105
107
|
test_files:
|
106
108
|
- test/helper.rb
|
109
|
+
- test/plugin/test_in_zmq_sub.rb
|
107
110
|
- test/plugin/test_out_zeromq_pub.rb
|