fluent-plugin-flume 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Licensed to Cloudera, Inc. under one
3
+ * or more contributor license agreements. See the NOTICE file
4
+ * distributed with this work for additional information
5
+ * regarding copyright ownership. Cloudera, Inc. licenses this file
6
+ * to you under the Apache License, Version 2.0 (the
7
+ * "License"); you may not use this file except in compliance
8
+ * with the License. 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
+ # flume.thrift
19
+ #
20
+ # This thrift interface and service defines a network transport mechanism to move events
21
+ # from one process/machine to another. At the moment this mirrors the fields of an event
22
+ # as defined in c.c.f.core.Event.java's code .
23
+ #
24
+ # This may change more fields are likely to be added, and the actual format is subject to change.
25
+
26
+ # The server has two rpc methods
27
+ # -- append: which sends an event to the server,
28
+ # -- close: shuts down this client's connection
29
+ #
30
+ # Currently append is oneway, requiring the thrift server to do flow control.
31
+
32
+ namespace java com.cloudera.flume.handlers.thrift
33
+
34
+ typedef i64 Timestamp
35
+
36
+ enum Priority {
37
+ FATAL = 0,
38
+ ERROR = 1,
39
+ WARN = 2,
40
+ INFO = 3,
41
+ DEBUG = 4,
42
+ TRACE = 5
43
+ }
44
+
45
+ enum EventStatus {
46
+ ACK = 0,
47
+ COMMITED = 1,
48
+ ERR = 2
49
+ }
50
+
51
+ struct ThriftFlumeEvent {
52
+ 1: Timestamp timestamp,
53
+ 2: Priority priority,
54
+ 3: binary body,
55
+ 4: i64 nanos,
56
+ 5: string host,
57
+ 6: map<string,binary> fieldss
58
+ }
59
+
60
+ # Instead of using thrift's serialization, we just assume the contents are serialized already.
61
+ struct RawEvent {
62
+ 1: binary raw
63
+ }
64
+
65
+ service ThriftFlumeEventServer {
66
+ oneway void append( 1:ThriftFlumeEvent evt ),
67
+ oneway void rawAppend( 1:RawEvent evt),
68
+ EventStatus ackedAppend( 1: ThriftFlumeEvent evt ),
69
+
70
+ void close(),
71
+ }
72
+
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Licensed to Cloudera, Inc. under one
3
+ * or more contributor license agreements. See the NOTICE file
4
+ * distributed with this work for additional information
5
+ * regarding copyright ownership. Cloudera, Inc. licenses this file
6
+ * to you under the Apache License, Version 2.0 (the
7
+ * "License"); you may not use this file except in compliance
8
+ * with the License. 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
+ # flume.thrift
19
+ #
20
+ # This thrift interface and service defines a network transport mechanism to move events
21
+ # from one process/machine to another. At the moment this mirrors the fields of an event
22
+ # as defined in c.c.f.core.Event.java's code .
23
+ #
24
+ # This may change more fields are likely to be added, and the actual format is subject to change.
25
+
26
+ # The server has two rpc methods
27
+ # -- append: which sends an event to the server,
28
+ # -- close: shuts down this client's connection
29
+ #
30
+ # Currently append is oneway, requiring the thrift server to do flow control.
31
+
32
+ namespace java com.cloudera.flume.handlers.thrift
33
+
34
+ typedef i64 Timestamp
35
+
36
+ enum Priority {
37
+ FATAL = 0,
38
+ ERROR = 1,
39
+ WARN = 2,
40
+ INFO = 3,
41
+ DEBUG = 4,
42
+ TRACE = 5
43
+ }
44
+
45
+ enum EventStatus {
46
+ ACK = 0,
47
+ COMMITED = 1,
48
+ ERR = 2
49
+ }
50
+
51
+ struct ThriftFlumeEvent {
52
+ 1: Timestamp timestamp,
53
+ 2: Priority priority,
54
+ 3: binary body,
55
+ 4: i64 nanos,
56
+ 5: string host,
57
+ 6: map<string,binary> fields
58
+ }
59
+
60
+ # Instead of using thrift's serialization, we just assume the contents are serialized already.
61
+ struct RawEvent {
62
+ 1: binary raw
63
+ }
64
+
65
+ service ThriftFlumeEventServer {
66
+ oneway void append( 1:ThriftFlumeEvent evt ),
67
+ oneway void rawAppend( 1:RawEvent evt),
68
+ EventStatus ackedAppend( 1: ThriftFlumeEvent evt ),
69
+
70
+ void close(),
71
+ }
72
+
@@ -0,0 +1,8 @@
1
+ #
2
+ # Autogenerated by Thrift
3
+ #
4
+ # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
5
+ #
6
+
7
+ require 'flume_types'
8
+
@@ -0,0 +1,71 @@
1
+ #
2
+ # Autogenerated by Thrift
3
+ #
4
+ # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
5
+ #
6
+
7
+
8
+ module Priority
9
+ FATAL = 0
10
+ ERROR = 1
11
+ WARN = 2
12
+ INFO = 3
13
+ DEBUG = 4
14
+ TRACE = 5
15
+ VALUE_MAP = {0 => "FATAL", 1 => "ERROR", 2 => "WARN", 3 => "INFO", 4 => "DEBUG", 5 => "TRACE"}
16
+ VALID_VALUES = Set.new([FATAL, ERROR, WARN, INFO, DEBUG, TRACE]).freeze
17
+ end
18
+
19
+ module EventStatus
20
+ ACK = 0
21
+ COMMITED = 1
22
+ ERR = 2
23
+ VALUE_MAP = {0 => "ACK", 1 => "COMMITED", 2 => "ERR"}
24
+ VALID_VALUES = Set.new([ACK, COMMITED, ERR]).freeze
25
+ end
26
+
27
+ class ThriftFlumeEvent
28
+ include ::Thrift::Struct, ::Thrift::Struct_Union
29
+ TIMESTAMP = 1
30
+ PRIORITY = 2
31
+ BODY = 3
32
+ NANOS = 4
33
+ HOST = 5
34
+ FIELDSS = 6
35
+
36
+ FIELDS = {
37
+ TIMESTAMP => {:type => ::Thrift::Types::I64, :name => 'timestamp'},
38
+ PRIORITY => {:type => ::Thrift::Types::I32, :name => 'priority', :enum_class => Priority},
39
+ BODY => {:type => ::Thrift::Types::STRING, :name => 'body', :binary => true},
40
+ NANOS => {:type => ::Thrift::Types::I64, :name => 'nanos'},
41
+ HOST => {:type => ::Thrift::Types::STRING, :name => 'host'},
42
+ FIELDSS => {:type => ::Thrift::Types::MAP, :name => 'fieldss', :key => {:type => ::Thrift::Types::STRING}, :value => {:type => ::Thrift::Types::STRING, :binary => true}}
43
+ }
44
+
45
+ def struct_fields; FIELDS; end
46
+
47
+ def validate
48
+ unless @priority.nil? || Priority::VALID_VALUES.include?(@priority)
49
+ raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Invalid value of field priority!')
50
+ end
51
+ end
52
+
53
+ ::Thrift::Struct.generate_accessors self
54
+ end
55
+
56
+ class RawEvent
57
+ include ::Thrift::Struct, ::Thrift::Struct_Union
58
+ RAW = 1
59
+
60
+ FIELDS = {
61
+ RAW => {:type => ::Thrift::Types::STRING, :name => 'raw', :binary => true}
62
+ }
63
+
64
+ def struct_fields; FIELDS; end
65
+
66
+ def validate
67
+ end
68
+
69
+ ::Thrift::Struct.generate_accessors self
70
+ end
71
+
@@ -0,0 +1,220 @@
1
+ #
2
+ # Autogenerated by Thrift
3
+ #
4
+ # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
5
+ #
6
+
7
+ require 'thrift'
8
+ require 'flume_types'
9
+
10
+ module ThriftFlumeEventServer
11
+ class Client
12
+ include ::Thrift::Client
13
+
14
+ def append(evt)
15
+ send_append(evt)
16
+ end
17
+
18
+ def send_append(evt)
19
+ send_message('append', Append_args, :evt => evt)
20
+ end
21
+ def rawAppend(evt)
22
+ send_rawAppend(evt)
23
+ end
24
+
25
+ def send_rawAppend(evt)
26
+ send_message('rawAppend', RawAppend_args, :evt => evt)
27
+ end
28
+ def ackedAppend(evt)
29
+ send_ackedAppend(evt)
30
+ return recv_ackedAppend()
31
+ end
32
+
33
+ def send_ackedAppend(evt)
34
+ send_message('ackedAppend', AckedAppend_args, :evt => evt)
35
+ end
36
+
37
+ def recv_ackedAppend()
38
+ result = receive_message(AckedAppend_result)
39
+ return result.success unless result.success.nil?
40
+ raise ::Thrift::ApplicationException.new(::Thrift::ApplicationException::MISSING_RESULT, 'ackedAppend failed: unknown result')
41
+ end
42
+
43
+ def close()
44
+ send_close()
45
+ recv_close()
46
+ end
47
+
48
+ def send_close()
49
+ send_message('close', Close_args)
50
+ end
51
+
52
+ def recv_close()
53
+ result = receive_message(Close_result)
54
+ return
55
+ end
56
+
57
+ end
58
+
59
+ class Processor
60
+ include ::Thrift::Processor
61
+
62
+ def process_append(seqid, iprot, oprot)
63
+ args = read_args(iprot, Append_args)
64
+ @handler.append(args.evt)
65
+ return
66
+ end
67
+
68
+ def process_rawAppend(seqid, iprot, oprot)
69
+ args = read_args(iprot, RawAppend_args)
70
+ @handler.rawAppend(args.evt)
71
+ return
72
+ end
73
+
74
+ def process_ackedAppend(seqid, iprot, oprot)
75
+ args = read_args(iprot, AckedAppend_args)
76
+ result = AckedAppend_result.new()
77
+ result.success = @handler.ackedAppend(args.evt)
78
+ write_result(result, oprot, 'ackedAppend', seqid)
79
+ end
80
+
81
+ def process_close(seqid, iprot, oprot)
82
+ args = read_args(iprot, Close_args)
83
+ result = Close_result.new()
84
+ @handler.close()
85
+ write_result(result, oprot, 'close', seqid)
86
+ end
87
+
88
+ end
89
+
90
+ # HELPER FUNCTIONS AND STRUCTURES
91
+
92
+ class Append_args
93
+ include ::Thrift::Struct, ::Thrift::Struct_Union
94
+ EVT = 1
95
+
96
+ FIELDS = {
97
+ EVT => {:type => ::Thrift::Types::STRUCT, :name => 'evt', :class => ThriftFlumeEvent}
98
+ }
99
+
100
+ def struct_fields; FIELDS; end
101
+
102
+ def validate
103
+ end
104
+
105
+ ::Thrift::Struct.generate_accessors self
106
+ end
107
+
108
+ class Append_result
109
+ include ::Thrift::Struct, ::Thrift::Struct_Union
110
+
111
+ FIELDS = {
112
+
113
+ }
114
+
115
+ def struct_fields; FIELDS; end
116
+
117
+ def validate
118
+ end
119
+
120
+ ::Thrift::Struct.generate_accessors self
121
+ end
122
+
123
+ class RawAppend_args
124
+ include ::Thrift::Struct, ::Thrift::Struct_Union
125
+ EVT = 1
126
+
127
+ FIELDS = {
128
+ EVT => {:type => ::Thrift::Types::STRUCT, :name => 'evt', :class => RawEvent}
129
+ }
130
+
131
+ def struct_fields; FIELDS; end
132
+
133
+ def validate
134
+ end
135
+
136
+ ::Thrift::Struct.generate_accessors self
137
+ end
138
+
139
+ class RawAppend_result
140
+ include ::Thrift::Struct, ::Thrift::Struct_Union
141
+
142
+ FIELDS = {
143
+
144
+ }
145
+
146
+ def struct_fields; FIELDS; end
147
+
148
+ def validate
149
+ end
150
+
151
+ ::Thrift::Struct.generate_accessors self
152
+ end
153
+
154
+ class AckedAppend_args
155
+ include ::Thrift::Struct, ::Thrift::Struct_Union
156
+ EVT = 1
157
+
158
+ FIELDS = {
159
+ EVT => {:type => ::Thrift::Types::STRUCT, :name => 'evt', :class => ThriftFlumeEvent}
160
+ }
161
+
162
+ def struct_fields; FIELDS; end
163
+
164
+ def validate
165
+ end
166
+
167
+ ::Thrift::Struct.generate_accessors self
168
+ end
169
+
170
+ class AckedAppend_result
171
+ include ::Thrift::Struct, ::Thrift::Struct_Union
172
+ SUCCESS = 0
173
+
174
+ FIELDS = {
175
+ SUCCESS => {:type => ::Thrift::Types::I32, :name => 'success', :enum_class => EventStatus}
176
+ }
177
+
178
+ def struct_fields; FIELDS; end
179
+
180
+ def validate
181
+ unless @success.nil? || EventStatus::VALID_VALUES.include?(@success)
182
+ raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Invalid value of field success!')
183
+ end
184
+ end
185
+
186
+ ::Thrift::Struct.generate_accessors self
187
+ end
188
+
189
+ class Close_args
190
+ include ::Thrift::Struct, ::Thrift::Struct_Union
191
+
192
+ FIELDS = {
193
+
194
+ }
195
+
196
+ def struct_fields; FIELDS; end
197
+
198
+ def validate
199
+ end
200
+
201
+ ::Thrift::Struct.generate_accessors self
202
+ end
203
+
204
+ class Close_result
205
+ include ::Thrift::Struct, ::Thrift::Struct_Union
206
+
207
+ FIELDS = {
208
+
209
+ }
210
+
211
+ def struct_fields; FIELDS; end
212
+
213
+ def validate
214
+ end
215
+
216
+ ::Thrift::Struct.generate_accessors self
217
+ end
218
+
219
+ end
220
+
@@ -0,0 +1,209 @@
1
+ require 'test/unit'
2
+ require 'fluent/test'
3
+ require 'fluent/plugin/in_flume'
4
+
5
+ class FlumeInputTest < Test::Unit::TestCase
6
+ CONFIG = %[
7
+ port 56790
8
+ bind 127.0.0.1
9
+ ]
10
+
11
+ def create_driver(conf=CONFIG)
12
+ Fluent::Test::InputTestDriver.new(Fluent::FlumeInput).configure(conf)
13
+ end
14
+
15
+ def test_configure
16
+ d = create_driver ''
17
+ assert_equal 56789, d.instance.port
18
+ assert_equal '0.0.0.0', d.instance.bind
19
+
20
+ d = create_driver
21
+ assert_equal 56790, d.instance.port
22
+ assert_equal '127.0.0.1', d.instance.bind
23
+ end
24
+
25
+ def test_time
26
+ d = create_driver
27
+
28
+ time = Time.parse("2011-01-02 13:14:15 UTC").to_i
29
+ Fluent::Engine.now = time
30
+
31
+ d.expect_emit "category", time, {"message"=>'aiueo'}
32
+ d.expect_emit "category", time, {"message"=>'aiueo'}
33
+
34
+ emits = [
35
+ ['tag1', time, {"message"=>'aiueo'}],
36
+ ['tag2', time, {"message"=>'aiueo'}]
37
+ ]
38
+
39
+ d.run do
40
+ emits.each { |tag, time, record|
41
+ send('tag', tag, time, record['message'])
42
+ sleep 0.01
43
+ }
44
+ end
45
+
46
+ d2 = create_driver(CONFIG + %[
47
+ default_tag flume
48
+ ])
49
+
50
+ time = Time.parse("2011-01-02 13:14:15 UTC").to_i
51
+ Fluent::Engine.now = time
52
+
53
+ d2.expect_emit "flume", time, {"message"=>'aiueo'}
54
+ d2.expect_emit "flume", time, {"message"=>'aiueo'}
55
+
56
+ emits = [
57
+ ['tag1', time, {"message"=>'aiueo'}],
58
+ ['tag2', time, {"message"=>'aiueo'}]
59
+ ]
60
+
61
+ d2.run do
62
+ emits.each { |tag, time, record|
63
+ send('tag', tag, time, record['message'])
64
+ sleep 0.01
65
+ }
66
+ end
67
+
68
+ d3 = create_driver(CONFIG + %[
69
+ tag_field flume
70
+ ])
71
+
72
+ time = Time.parse("2011-01-02 13:14:15 UTC").to_i
73
+ Fluent::Engine.now = time
74
+
75
+ d3.expect_emit "tag1", time, {"message"=>'aiueo'}
76
+ d3.expect_emit "tag2", time, {"message"=>'aiueo'}
77
+
78
+ emits = [
79
+ ['tag1', time, {"message"=>'aiueo'}],
80
+ ['tag2', time, {"message"=>'aiueo'}]
81
+ ]
82
+
83
+ d3.run do
84
+ emits.each { |tag, time, record|
85
+ send('flume', tag, time, record['message'])
86
+ sleep 0.01
87
+ }
88
+ end
89
+
90
+ d4 = create_driver(CONFIG + %[
91
+ default_tag fluent
92
+ tag_field flume
93
+ ])
94
+
95
+ time = Time.parse("2011-01-02 13:14:15 UTC").to_i
96
+ Fluent::Engine.now = time
97
+
98
+ d4.expect_emit "tag1", time, {"message"=>'aiueo'}
99
+ d4.expect_emit "tag2", time, {"message"=>'aiueo'}
100
+
101
+ emits = [
102
+ ['tag1', time, {"message"=>'aiueo'}],
103
+ ['tag2', time, {"message"=>'aiueo'}]
104
+ ]
105
+
106
+ d4.run do
107
+ emits.each { |tag, time, record|
108
+ send('flume', tag, time, record['message'])
109
+ sleep 0.01
110
+ }
111
+ end
112
+ end
113
+
114
+ def test_add_prefix
115
+ d = create_driver(CONFIG + %[
116
+ add_prefix flume
117
+ tag_field category
118
+ ])
119
+
120
+ time = Time.parse("2011-01-02 13:14:15 UTC").to_i
121
+ Fluent::Engine.now = time
122
+
123
+ d.expect_emit "flume.tag1", time, {"message"=>'aiueo'}
124
+ d.expect_emit "flume.tag2", time, {"message"=>'aiueo'}
125
+
126
+ emits = [
127
+ ['tag1', time, {"message"=>'aiueo'}],
128
+ ['tag2', time, {"message"=>'aiueo'}],
129
+ ]
130
+ d.run do
131
+ emits.each { |tag, time, record|
132
+ send('category', tag, time, record['message'])
133
+ sleep 0.01
134
+ }
135
+ end
136
+
137
+ d2 = create_driver(CONFIG + %[
138
+ add_prefix flume.input
139
+ tag_field category
140
+ ])
141
+
142
+ time = Time.parse("2011-01-02 13:14:15 UTC").to_i
143
+ Fluent::Engine.now = time
144
+
145
+ d2.expect_emit "flume.input.tag3", time, {"message"=>'aiueo'}
146
+ d2.expect_emit "flume.input.tag4", time, {"message"=>'aiueo'}
147
+
148
+ emits = [
149
+ ['tag3', time, {"message"=>'aiueo'}],
150
+ ['tag4', time, {"message"=>'aiueo'}],
151
+ ]
152
+ d2.run do
153
+ emits.each { |tag, time, record|
154
+ send('category', tag, time, record['message'])
155
+ sleep 0.01
156
+ }
157
+ end
158
+ end
159
+
160
+ def test_message_format_json
161
+ d = create_driver(CONFIG + %[
162
+ tag_field category
163
+ message_format json
164
+ ])
165
+ assert_equal 'json', d.instance.message_format
166
+
167
+ time = Time.parse("2011-01-02 13:14:15 UTC").to_i
168
+ Fluent::Engine.now = time
169
+
170
+ d.expect_emit "tag1", time, {"a"=>1}
171
+ d.expect_emit "tag2", time, {"a"=>1, "b"=>2}
172
+ d.expect_emit "tag3", time, {"a"=>1, "b"=>2, "c"=>3}
173
+
174
+ emits = [
175
+ ['tag1', time, {"a"=>1}.to_json],
176
+ ['tag2', time, {"a"=>1, "b"=>2}.to_json],
177
+ ['tag3', time, {"a"=>1, "b"=>2, "c"=>3}.to_json],
178
+ ]
179
+ d.run do
180
+ emits.each { |tag, time, message|
181
+ send('category', tag, time, message)
182
+ sleep 0.01
183
+ }
184
+ end
185
+ end
186
+
187
+ def setup
188
+ Fluent::FlumeInput.new
189
+ Fluent::Test.setup
190
+ end
191
+
192
+ def send(tag_field, tag, time, msg)
193
+ socket = Thrift::Socket.new '127.0.0.1', 56790
194
+ #transport = Thrift::Socket.new '127.0.0.1', 56790
195
+ transport = Thrift::BufferedTransport.new socket
196
+ protocol = Thrift::BinaryProtocol.new transport
197
+ client = ThriftFlumeEventServer::Client.new protocol
198
+ transport.open
199
+ raw_sock = socket.to_io
200
+ raw_sock.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
201
+
202
+ entry = ThriftFlumeEvent.new(:body=>msg.to_s,
203
+ :priority=>Priority::INFO,
204
+ :timestamp=>time,
205
+ :fieldss=>{tag_field=>tag})
206
+ client.append entry
207
+ transport.close
208
+ end
209
+ end