fluent-plugin-flume 0.1.0

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/AUTHORS ADDED
@@ -0,0 +1 @@
1
+ Muga Nishizawa <muga.nishizawa _at_ gmail.com>
@@ -0,0 +1 @@
1
+ Release 0.1.0 - 2012/05/02
@@ -0,0 +1,105 @@
1
+ = Flume input/output plugin for Fluent
2
+
3
+ == Overview
4
+
5
+ This is a plugin for fluentd[https://github.com/fluentd] event collector. This plugin adds the Flume[https://github.com/apache/flume] compatible interface to fluentd.
6
+
7
+ == What's Flume?
8
+
9
+ Flume[https://github.com/apache/flume] is a distributed, reliable, and available service for efficiently collecting, aggregating, and moving large amounts of log data.
10
+
11
+ It uses Thrift[http://thrift.apache.org/], a cross-language RPC framework, to communicate between clients and servers.
12
+
13
+ == What's Flume plugin for fluent?
14
+
15
+ The Flume plugin for fluentd, which enables fluentd to talk the Flume protocol. Flume protocol is defined as follows, in Thrift-IDL format:
16
+
17
+ typedef i64 Timestamp
18
+
19
+ enum Priority {
20
+ FATAL = 0,
21
+ ERROR = 1,
22
+ WARN = 2,
23
+ INFO = 3,
24
+ DEBUG = 4,
25
+ TRACE = 5
26
+ }
27
+
28
+ enum EventStatus {
29
+ ACK = 0,
30
+ COMMITED = 1,
31
+ ERR = 2
32
+ }
33
+
34
+ struct ThriftFlumeEvent {
35
+ 1: Timestamp timestamp,
36
+ 2: Priority priority,
37
+ 3: binary body,
38
+ 4: i64 nanos,
39
+ 5: string host,
40
+ 6: map<string,binary> fields
41
+ }
42
+
43
+ # Instead of using thrift's serialization, we just assume the contents are serialized already.
44
+ struct RawEvent {
45
+ 1: binary raw
46
+ }
47
+
48
+ service ThriftFlumeEventServer {
49
+ oneway void append( 1:ThriftFlumeEvent evt ),
50
+ oneway void rawAppend( 1:RawEvent evt),
51
+ EventStatus ackedAppend( 1: ThriftFlumeEvent evt ),
52
+
53
+ void close(),
54
+ }
55
+
56
+ A value that is stored in the ThriftFlumeEvent.fields map is used as fluentd 'tag'. A key of the value enables be specified by users as configuration parameter.
57
+
58
+ == How to use?
59
+
60
+ fluent-plugin-flume contains both input and output.
61
+
62
+ == Flume Input
63
+
64
+ Please add the following configurations to fluent.conf.
65
+
66
+ # Flume input
67
+ <source>
68
+ type flume
69
+ port 56789
70
+ </source>
71
+
72
+ These options are supported.
73
+
74
+ * port: port number (default: 56789)
75
+ * bind: bind address (default: 0.0.0.0)
76
+ * server_type: server architecture either in 'simple', 'threaded', 'thread_pool', (default: simple)
77
+ * is_framed: use framed protocol or not (default: false)
78
+ * tag_field: key name of fluentd 'tag' that is stored in ThriftFlumeEvent.fields (default: nil)
79
+ * default_tag: default fluentd 'tag' (default: 'category')
80
+ * add_prefix: prefix string, added to the tag (default: nil)
81
+
82
+ == Flume Output
83
+
84
+ Please add the following configurations to fluent.conf. This allows fluentd to output its logs into another Flume server. Note that fluentd conveys semi-structured data while Flume conveys unstructured data. Thus the plugin translates semi-structured data into JSON data and conveys it to Flume.
85
+
86
+ # Flume output
87
+ <match *>
88
+ type flume
89
+ host flume-host.local
90
+ port 56789
91
+ </match>
92
+
93
+ These options are supported.
94
+
95
+ * host: host name or address (default: localhost)
96
+ * port: port number (default: 35863)
97
+ * timeout: thrift protocol timeout (default: 30)
98
+ * remove_prefix: prefix string, removed from the tag (default: nil)
99
+
100
+ == Contributors
101
+
102
+ == Copyright
103
+
104
+ Copyright:: Copyright (c) 2012 Treasure Data, Inc.
105
+ License:: Apache License, Version 2.0
@@ -0,0 +1,57 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/clean'
4
+
5
+ begin
6
+ require 'jeweler'
7
+ Jeweler::Tasks.new do |gemspec|
8
+ gemspec.name = "fluent-plugin-flume"
9
+ gemspec.summary = "Flume Input/Output plugin for Fluentd event collector"
10
+ gemspec.author = "Muga Nishizawa"
11
+ gemspec.email = "muga.nishizawa@gmail.com"
12
+ gemspec.homepage = "https://github.com/muga/fluent-plugin-flume"
13
+ gemspec.has_rdoc = false
14
+ gemspec.require_paths = ["lib"]
15
+ gemspec.add_dependency "fluentd", "~> 0.10.16"
16
+ gemspec.add_dependency "thrift", "~> 0.6.0"
17
+ gemspec.test_files = Dir["test/**/*.rb"]
18
+ gemspec.files = Dir["bin/**/*", "lib/**/*", "test/**/*.rb"] +
19
+ %w[example.conf VERSION AUTHORS Rakefile fluent-plugin-flume.gemspec]
20
+ gemspec.executables = ['fluent-flume-remote']
21
+ end
22
+ Jeweler::GemcutterTasks.new
23
+ rescue LoadError
24
+ puts "Jeweler not available. Install it with: gem install jeweler"
25
+ end
26
+
27
+ task "thrift_gen" do
28
+ system "mkdir -p tmp"
29
+ system "thrift --gen rb -o tmp lib/fluent/plugin/thrift/flume.thrift"
30
+ system "mv tmp/gen-rb/* lib/fluent/plugin/thrift/"
31
+ system "rm -fR tmp"
32
+ end
33
+
34
+ Rake::TestTask.new(:test) do |t|
35
+ t.test_files = Dir['test/plugin/*.rb']
36
+ t.ruby_opts = ['-rubygems'] if defined? Gem
37
+ t.ruby_opts << '-I.'
38
+ end
39
+
40
+ #VERSION_FILE = "lib/fluent/version.rb"
41
+ #
42
+ #file VERSION_FILE => ["VERSION"] do |t|
43
+ # version = File.read("VERSION").strip
44
+ # File.open(VERSION_FILE, "w") {|f|
45
+ # f.write <<EOF
46
+ #module Fluent
47
+ #
48
+ #VERSION = '#{version}'
49
+ #
50
+ #end
51
+ #EOF
52
+ # }
53
+ #end
54
+ #
55
+ #task :default => [VERSION_FILE, :build]
56
+
57
+ task :default => [:build]
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+ require 'thrift'
3
+ require 'json'
4
+ $:.unshift File.join(File.dirname(__FILE__), '../lib/fluent/plugin/thrift')
5
+ require 'flume_types'
6
+ require 'flume_constants'
7
+ require 'thrift_flume_event_server'
8
+
9
+ host = 'localhost'
10
+ port = 3586
11
+
12
+ socket = Thrift::Socket.new host, port.to_i
13
+ transport = Thrift::BufferedTransport.new socket
14
+ protocol = Thrift::BinaryProtocol.new transport
15
+ client = ThriftFlumeEventServer::Client.new protocol
16
+ transport.open
17
+
18
+ # 2011/09/02 Kazuki Ohta <kazuki.ohta@gmail.com>
19
+ # explicitly specify TCP_NODELAY for low-latency communication.
20
+ raw_sock = socket.to_io
21
+ raw_sock.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
22
+
23
+ entry = ThriftFlumeEvent.new(:body=>{'a'=>'b'}.to_json,
24
+ :priority=>Priority::INFO,
25
+ :timestamp=>(Time.now.to_i * 1000))
26
+ client.append entry
27
+
28
+ transport.close
@@ -0,0 +1,56 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "fluent-plugin-flume"
8
+ s.version = "0.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Muga Nishizawa"]
12
+ s.date = "2012-04-07"
13
+ s.email = "muga.nishizawa@gmail.com"
14
+ s.executables = ["fluent-flume-remote"]
15
+ s.extra_rdoc_files = [
16
+ "ChangeLog",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ "AUTHORS",
21
+ "Rakefile",
22
+ "VERSION",
23
+ "bin/fluent-flume-remote",
24
+ "example.conf",
25
+ "fluent-plugin-flume.gemspec",
26
+ "lib/fluent/plugin/in_flume.rb",
27
+ "lib/fluent/plugin/out_flume.rb",
28
+ "lib/fluent/plugin/thrift/flume.thrift",
29
+ "lib/fluent/plugin/thrift/flume.thrift.orig",
30
+ "lib/fluent/plugin/thrift/flume_constants.rb",
31
+ "lib/fluent/plugin/thrift/flume_types.rb",
32
+ "lib/fluent/plugin/thrift/thrift_flume_event_server.rb",
33
+ "test/plugin/out_flume.rb"
34
+ ]
35
+ s.homepage = "https://github.com/muga/fluent-plugin-flume"
36
+ s.require_paths = ["lib"]
37
+ s.rubygems_version = "1.8.15"
38
+ s.summary = "Flume Input/Output plugin for Fluentd event collector"
39
+ s.test_files = ["test/plugin/out_flume.rb"]
40
+
41
+ if s.respond_to? :specification_version then
42
+ s.specification_version = 3
43
+
44
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
45
+ s.add_runtime_dependency(%q<fluentd>, ["~> 0.10.16"])
46
+ s.add_runtime_dependency(%q<thrift>, ["~> 0.6.0"])
47
+ else
48
+ s.add_dependency(%q<fluentd>, ["~> 0.10.16"])
49
+ s.add_dependency(%q<thrift>, ["~> 0.6.0"])
50
+ end
51
+ else
52
+ s.add_dependency(%q<fluentd>, ["~> 0.10.16"])
53
+ s.add_dependency(%q<thrift>, ["~> 0.6.0"])
54
+ end
55
+ end
56
+
@@ -0,0 +1,164 @@
1
+ #
2
+ # Fluent
3
+ #
4
+ # Copyright (C) 2012 Muga Nishizawa
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ module Fluent
19
+
20
+ class FlumeInput < Input
21
+ Plugin.register_input('flume', self)
22
+
23
+ config_param :port, :integer, :default => 56789
24
+ config_param :bind, :string, :default => '0.0.0.0'
25
+ config_param :server_type, :string, :default => 'simple'
26
+ config_param :is_framed, :bool, :default => false
27
+ config_param :body_size_limit, :size, :default => 32*1024*1024 # TODO default
28
+ config_param :tag_field, :string, :default => nil
29
+ config_param :default_tag, :string, :default => 'category'
30
+ config_param :add_prefix, :string, :default => nil
31
+ config_param :message_format, :string, :default => 'text'
32
+
33
+ def initialize
34
+ require 'thrift'
35
+ $:.unshift File.join(File.dirname(__FILE__), 'thrift')
36
+ require 'flume_types'
37
+ require 'flume_constants'
38
+ require 'thrift_flume_event_server'
39
+
40
+ super
41
+ end
42
+
43
+ def configure(conf)
44
+ super
45
+ end
46
+
47
+ def start
48
+ $log.debug "listening flume on #{@bind}:#{@port}"
49
+
50
+ handler = FluentFlumeHandler.new
51
+ handler.tag_field = @tag_field
52
+ handler.default_tag = @default_tag
53
+ handler.add_prefix = @add_prefix
54
+ handler.message_format = @message_format
55
+ processor = ThriftFlumeEventServer::Processor.new handler
56
+
57
+ @transport = Thrift::ServerSocket.new @bind, @port
58
+ unless @is_framed
59
+ transport_factory = Thrift::BufferedTransportFactory.new
60
+ else
61
+ raise ConfigError, "in_flume: unsupported is_framed '#{@is_framed}'"
62
+ end
63
+
64
+ unless ['text', 'json'].include? @message_format
65
+ raise 'Unknown format: message_format=#{@message_format}'
66
+ end
67
+
68
+ # 2011/09/29 Kazuki Ohta <kazuki.ohta@gmail.com>
69
+ # This section is a workaround to set strict_read and strict_write option.
70
+ # Ruby-Thrift 0.7 set them both 'true' in default, but Flume protocol set
71
+ # them both 'false'.
72
+ protocol_factory = Thrift::BinaryProtocolFactory.new
73
+ protocol_factory.instance_eval {|obj|
74
+ def get_protocol(trans) # override
75
+ return Thrift::BinaryProtocol.new(trans,
76
+ strict_read=false,
77
+ strict_write=false)
78
+ end
79
+ }
80
+
81
+ case @server_type
82
+ when 'simple'
83
+ @server = Thrift::SimpleServer.new processor, @transport, transport_factory, protocol_factory
84
+ when 'threaded'
85
+ @server = Thrift::ThreadedServer.new processor, @transport, transport_factory, protocol_factory
86
+ when 'thread_pool'
87
+ @server = Thrift::ThreadPoolServer.new processor, @transport, transport_factory, protocol_factory
88
+ else
89
+ raise ConfigError, "in_flume: unsupported server_type '#{@server_type}'"
90
+ end
91
+ @thread = Thread.new(&method(:run))
92
+ end
93
+
94
+ def shutdown
95
+ @transport.close unless @transport.closed? # TODO
96
+ #@thread.join # TODO
97
+ end
98
+
99
+ def run
100
+ $log.debug "starting server: #{@server}"
101
+ @server.serve
102
+ rescue
103
+ $log.error "unexpected error", :error=>$!.to_s
104
+ $log.error_backtrace
105
+ end
106
+
107
+ class FluentFlumeHandler
108
+ attr_accessor :tag_field
109
+ attr_accessor :default_tag
110
+ attr_accessor :add_prefix
111
+ attr_accessor :message_format
112
+
113
+ def append(evt)
114
+ begin
115
+ record = create_record(evt)
116
+ if @tag_field
117
+ tag = evt.fieldss[@tag_field] || @default_tag
118
+ unless tag
119
+ return # ignore
120
+ end
121
+ else
122
+ tag = @default_tag
123
+ end
124
+ timestamp = evt.timestamp.to_i
125
+ if @add_prefix
126
+ Engine.emit(@add_prefix + '.' + tag, timestamp, record)
127
+ else
128
+ Engine.emit(tag, timestamp, record)
129
+ end
130
+ rescue => e
131
+ $log.error "unexpected error", :error=>$!.to_s
132
+ $log.error_backtrace
133
+ end
134
+ end
135
+
136
+ def rawAppend(evt)
137
+ $log.error "rawAppend is not implemented yet: #{evt}"
138
+ end
139
+
140
+ def ackedAppend(evt)
141
+ $log.error "ackedAppend is not implemented yet: #{evt}"
142
+ EventStatus::OK
143
+ end
144
+
145
+ def close()
146
+ end
147
+
148
+ private
149
+ def create_record(evt)
150
+ case @message_format
151
+ when 'text'
152
+ return {'message'=>evt.body.force_encoding('UTF-8')}
153
+ when 'json'
154
+ js = JSON.parse(evt.body.force_encoding('UTF-8'))
155
+ raise 'body must be a Hash, if json_body=true' unless js.is_a?(Hash)
156
+ return js
157
+ else
158
+ raise 'Invalid format: #{@message_format}'
159
+ end
160
+ end
161
+ end
162
+ end
163
+
164
+ end
@@ -0,0 +1,96 @@
1
+ #
2
+ # Fluent
3
+ #
4
+ # Copyright (C) 2012 Muga Nishizawa
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ module Fluent
19
+
20
+ class FlumeOutput < BufferedOutput
21
+ Fluent::Plugin.register_output('flume', self)
22
+
23
+ config_param :host, :string, :default => 'localhost'
24
+ config_param :port, :integer, :default => 35863
25
+ config_param :timeout, :integer, :default => 30
26
+ config_param :remove_prefix, :string, :default => nil
27
+ config_param :default_category, :string, :default => 'unknown'
28
+
29
+ def initialize
30
+ require 'thrift'
31
+ $:.unshift File.join(File.dirname(__FILE__), 'thrift')
32
+ require 'flume_types'
33
+ require 'flume_constants'
34
+ require 'thrift_flume_event_server'
35
+
36
+ super
37
+ end
38
+
39
+ def configure(conf)
40
+ super
41
+ end
42
+
43
+ def start
44
+ super
45
+
46
+ if @remove_prefix
47
+ @removed_prefix_string = @remove_prefix + '.'
48
+ @removed_length = @removed_prefix_string.length
49
+ end
50
+ end
51
+
52
+ def shutdown
53
+ super
54
+ end
55
+
56
+ def format(tag, time, record)
57
+ if @remove_prefix and
58
+ ( (tag[0, @removed_length] == @removed_prefix_string and tag.length > @removed_length) or
59
+ tag == @remove_prefix)
60
+ [(tag[@removed_length..-1] || @default_category), time, record].to_msgpack
61
+ else
62
+ [tag, time, record].to_msgpack
63
+ end
64
+ end
65
+
66
+ def write(chunk)
67
+ records = []
68
+ chunk.msgpack_each { |arr|
69
+ records << arr
70
+ }
71
+
72
+ transport = Thrift::Socket.new @host, @port, @timeout
73
+ #transport = Thrift::FramedTransport.new socket
74
+ #protocol = Thrift::BinaryProtocol.new transport, false, false
75
+ protocol = Thrift::BinaryProtocol.new transport
76
+ client = ThriftFlumeEventServer::Client.new protocol
77
+
78
+ transport.open
79
+ $log.debug "thrift client opend: #{client}"
80
+ begin
81
+ records.each { |r|
82
+ tag, time, record = r
83
+ entry = ThriftFlumeEvent.new(:body=>record.to_json.to_s.force_encoding('UTF-8'),
84
+ :priority=>Priority::INFO,
85
+ :timestamp=>time,
86
+ :fieldss=>{'category'=>tag})
87
+ client.append entry
88
+ }
89
+ ensure
90
+ $log.debug "thrift client closing: #{client}"
91
+ transport.close
92
+ end
93
+ end
94
+ end
95
+
96
+ end