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.
@@ -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