fluent-plugin-kafka 0.17.0 → 0.17.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 15805411e4029813123b9b636b6faadb937cf38c9841adcd9d998a0f54d8b687
4
- data.tar.gz: 75fef11595c86beb4a54d2d2ff659f77075e328426463f7ef830982c9724ff16
3
+ metadata.gz: 230266adf4ba3d77b8b2fd743863377c4dd2532297f45e4489df62bfcbee1db5
4
+ data.tar.gz: 4b82d2f33bbbb3294f547a7af154783c432c6316f1155a0e3a822361232743e4
5
5
  SHA512:
6
- metadata.gz: db08ffbde4fe36ce38abe6eebf83e6e0dc157bc5d8dd95141ef911d0c5b59074a208c3a8f836e258ffddc34f73fda303006eaef1cd8fc12ae4dd4c79d101d0c7
7
- data.tar.gz: 9b3b098b1bc58654924d50ac8d685af007763942c1cc68187bbd6d39510a27ca31ae795df0de312d97ad67463ae0c428882118640fce052119288e27e3fb0df5
6
+ metadata.gz: 9321cd8bd10dcd603b653c101e6695b7071d7b9dc61fb49bc79248b22cbe9f1db46416c40132243c9862e9c4a3888c818f7a8906906ccf0dda545032f2ac53fd
7
+ data.tar.gz: 832517ac39c4f775d95775454b07bc0f3ecc2f27b1c0bd4c3f8025e795dfbe869d3e349084f868bdb54cfa72188d4cbd96173192f728fae4281f7f84e5d973aa
@@ -5,6 +5,8 @@ on:
5
5
  jobs:
6
6
  build:
7
7
  runs-on: ${{ matrix.os }}
8
+ env:
9
+ USE_RDKAFKA: 1
8
10
  strategy:
9
11
  fail-fast: false
10
12
  matrix:
data/ChangeLog CHANGED
@@ -1,3 +1,8 @@
1
+ Release 0.17.1 - 2021/09/24
2
+ * out_rdkafka/out_rdkafka2: Support rdkafka 0.9.0 or later
3
+ * out_rdkafka/out_rdkafka2: Add `exclude_fields` parameter
4
+ * out_kafka2.rb: Fix one more Ruby 3.0 keyword arguments issue
5
+
1
6
  Release 0.17.0 - 2021/08/30
2
7
  * out_kafka/out_kafka_buffered/out_kafka2: Provide murmur2 partitioner hash function choice
3
8
  * in_kafka/in_kafka_group/out_kafka/out_kafka_buffered/out_kafka2: Use Ruby Kafka's ssl_ca_cert_file_path parameter to feed the CA certs
data/Gemfile CHANGED
@@ -2,3 +2,5 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in fluent-plugin-kafka.gemspec
4
4
  gemspec
5
+
6
+ gem 'rdkafka', '>= 0.6.0' if ENV["USE_RDKAFKA"]
data/README.md CHANGED
@@ -40,14 +40,14 @@ If you want to use zookeeper related parameters, you also need to install zookee
40
40
 
41
41
  Set path to SSL related files. See [Encryption and Authentication using SSL](https://github.com/zendesk/ruby-kafka#encryption-and-authentication-using-ssl) for more detail.
42
42
 
43
- #### SASL authentication
43
+ #### SASL authentication
44
44
 
45
45
  ##### with GSSAPI
46
46
 
47
47
  - principal
48
48
  - keytab
49
49
 
50
- Set principal and path to keytab for SASL/GSSAPI authentication.
50
+ Set principal and path to keytab for SASL/GSSAPI authentication.
51
51
  See [Authentication using SASL](https://github.com/zendesk/ruby-kafka#authentication-using-sasl) for more details.
52
52
 
53
53
  ##### with Plain/SCRAM
@@ -57,7 +57,7 @@ See [Authentication using SASL](https://github.com/zendesk/ruby-kafka#authentica
57
57
  - scram_mechanism
58
58
  - sasl_over_ssl
59
59
 
60
- Set username, password, scram_mechanism and sasl_over_ssl for SASL/Plain or Scram authentication.
60
+ Set username, password, scram_mechanism and sasl_over_ssl for SASL/Plain or Scram authentication.
61
61
  See [Authentication using SASL](https://github.com/zendesk/ruby-kafka#authentication-using-sasl) for more details.
62
62
 
63
63
  ### Input plugin (@type 'kafka')
@@ -316,6 +316,23 @@ The Kafka message will have a header of source_ip=12.7.0.0.1.
316
316
 
317
317
  The configuration format is jsonpath. It is descibed in https://docs.fluentd.org/plugin-helper-overview/api-plugin-helper-record_accessor
318
318
 
319
+ #### Excluding fields
320
+ Fields can be excluded from output data. Only works for kafka2 and rdkafka2 output plugin.
321
+
322
+ Fields must be specified using an array of dot notation `$.`, for example:
323
+
324
+ <match app.**>
325
+ @type kafka2
326
+ [...]
327
+ exclude_fields $.source.ip,$.HTTP_FOO
328
+ <match>
329
+
330
+ This config can be used to remove fields used on another configs.
331
+
332
+ For example, `$.source.ip` can be extracted with config `headers_from_record` and excluded from message payload.
333
+
334
+ > Using this config to remove unused fields is discouraged. A [filter plugin](https://docs.fluentd.org/v/0.12/filter) can be used for this purpose.
335
+
319
336
  ### Buffered output plugin
320
337
 
321
338
  This plugin uses ruby-kafka producer for writing data. This plugin is for v0.12. If you use v1, see `kafka2`.
@@ -13,7 +13,7 @@ Gem::Specification.new do |gem|
13
13
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
14
  gem.name = "fluent-plugin-kafka"
15
15
  gem.require_paths = ["lib"]
16
- gem.version = '0.17.0'
16
+ gem.version = '0.17.1'
17
17
  gem.required_ruby_version = ">= 2.1.0"
18
18
 
19
19
  gem.add_dependency "fluentd", [">= 0.10.58", "< 2"]
@@ -71,6 +71,7 @@ class Fluent::KafkaInput < Fluent::Input
71
71
  require 'kafka'
72
72
 
73
73
  @time_parser = nil
74
+ @zookeeper = nil
74
75
  end
75
76
 
76
77
  def configure(conf)
@@ -88,6 +88,7 @@ DESC
88
88
  require 'kafka'
89
89
 
90
90
  @kafka = nil
91
+ @field_separator = nil
91
92
  end
92
93
 
93
94
  def refresh_client
@@ -42,6 +42,8 @@ DESC
42
42
  :desc => 'Set true to remove message key from data'
43
43
  config_param :exclude_topic_key, :bool, :default => false,
44
44
  :desc => 'Set true to remove topic name key from data'
45
+ config_param :exclude_fields, :array, :default => [], value_type: :string,
46
+ :desc => 'Fields to remove from data where the value is a jsonpath to a record value'
45
47
  config_param :use_event_time, :bool, :default => false, :desc => 'Use fluentd event time for kafka create_time'
46
48
  config_param :headers, :hash, default: {}, symbolize_keys: true, value_type: :string,
47
49
  :desc => 'Kafka message headers'
@@ -177,6 +179,10 @@ DESC
177
179
  @headers_from_record.each do |key, value|
178
180
  @headers_from_record_accessors[key] = record_accessor_create(value)
179
181
  end
182
+
183
+ @exclude_field_accessors = @exclude_fields.map do |field|
184
+ record_accessor_create(field)
185
+ end
180
186
  end
181
187
 
182
188
  def multi_workers_ready?
@@ -235,7 +241,7 @@ DESC
235
241
  mutate_headers = !@headers_from_record_accessors.empty?
236
242
 
237
243
  begin
238
- producer = @kafka.topic_producer(topic, @producer_opts)
244
+ producer = @kafka.topic_producer(topic, **@producer_opts)
239
245
  chunk.msgpack_each { |time, record|
240
246
  begin
241
247
  record = inject_values_to_record(tag, time, record)
@@ -253,6 +259,12 @@ DESC
253
259
  headers = base_headers
254
260
  end
255
261
 
262
+ unless @exclude_fields.empty?
263
+ @exclude_field_accessors.each do |exclude_field_accessor|
264
+ exclude_field_accessor.delete(record)
265
+ end
266
+ end
267
+
256
268
  record_buf = @formatter_proc.call(tag, time, record)
257
269
  record_buf_bytes = record_buf.bytesize
258
270
  if @max_send_limit_bytes && record_buf_bytes > @max_send_limit_bytes
@@ -107,6 +107,7 @@ DESC
107
107
  @kafka = nil
108
108
  @producers = {}
109
109
  @producers_mutex = Mutex.new
110
+ @field_separator = nil
110
111
  end
111
112
 
112
113
  def multi_workers_ready?
@@ -91,23 +91,29 @@ DESC
91
91
  def configure(conf)
92
92
  super
93
93
  log.instance_eval {
94
- def add(level, &block)
95
- return unless block
94
+ def add(level, message = nil)
95
+ if message.nil?
96
+ if block_given?
97
+ message = yield
98
+ else
99
+ return
100
+ end
101
+ end
96
102
 
97
103
  # Follow rdkakfa's log level. See also rdkafka-ruby's bindings.rb: https://github.com/appsignal/rdkafka-ruby/blob/e5c7261e3f2637554a5c12b924be297d7dca1328/lib/rdkafka/bindings.rb#L117
98
104
  case level
99
105
  when Logger::FATAL
100
- self.fatal(block.call)
106
+ self.fatal(message)
101
107
  when Logger::ERROR
102
- self.error(block.call)
108
+ self.error(message)
103
109
  when Logger::WARN
104
- self.warn(block.call)
110
+ self.warn(message)
105
111
  when Logger::INFO
106
- self.info(block.call)
112
+ self.info(message)
107
113
  when Logger::DEBUG
108
- self.debug(block.call)
114
+ self.debug(message)
109
115
  else
110
- self.trace(block.call)
116
+ self.trace(message)
111
117
  end
112
118
  end
113
119
  }
@@ -56,6 +56,8 @@ DESC
56
56
  :desc => <<-DESC
57
57
  Set true to remove topic key from data
58
58
  DESC
59
+ config_param :exclude_fields, :array, :default => [], value_type: :string,
60
+ :desc => 'Fields to remove from data where the value is a jsonpath to a record value'
59
61
  config_param :headers, :hash, default: {}, symbolize_keys: true, value_type: :string,
60
62
  :desc => 'Kafka message headers'
61
63
  config_param :headers_from_record, :hash, default: {}, symbolize_keys: true, value_type: :string,
@@ -110,23 +112,29 @@ DESC
110
112
  def configure(conf)
111
113
  super
112
114
  log.instance_eval {
113
- def add(level, &block)
114
- return unless block
115
+ def add(level, message = nil)
116
+ if message.nil?
117
+ if block_given?
118
+ message = yield
119
+ else
120
+ return
121
+ end
122
+ end
115
123
 
116
124
  # Follow rdkakfa's log level. See also rdkafka-ruby's bindings.rb: https://github.com/appsignal/rdkafka-ruby/blob/e5c7261e3f2637554a5c12b924be297d7dca1328/lib/rdkafka/bindings.rb#L117
117
125
  case level
118
126
  when Logger::FATAL
119
- self.fatal(block.call)
127
+ self.fatal(message)
120
128
  when Logger::ERROR
121
- self.error(block.call)
129
+ self.error(message)
122
130
  when Logger::WARN
123
- self.warn(block.call)
131
+ self.warn(message)
124
132
  when Logger::INFO
125
- self.info(block.call)
133
+ self.info(message)
126
134
  when Logger::DEBUG
127
- self.debug(block.call)
135
+ self.debug(message)
128
136
  else
129
- self.trace(block.call)
137
+ self.trace(message)
130
138
  end
131
139
  end
132
140
  }
@@ -158,6 +166,10 @@ DESC
158
166
  @headers_from_record.each do |key, value|
159
167
  @headers_from_record_accessors[key] = record_accessor_create(value)
160
168
  end
169
+
170
+ @exclude_field_accessors = @exclude_fields.map do |field|
171
+ record_accessor_create(field)
172
+ end
161
173
  end
162
174
 
163
175
  def build_config
@@ -305,6 +317,12 @@ DESC
305
317
  headers[key] = header_accessor.call(record)
306
318
  end
307
319
 
320
+ unless @exclude_fields.empty?
321
+ @exclude_field_accessors.each do |exclude_field_acessor|
322
+ exclude_field_acessor.delete(record)
323
+ end
324
+ end
325
+
308
326
  record_buf = @formatter_proc.call(tag, time, record)
309
327
  record_buf_bytes = record_buf.bytesize
310
328
  if @max_send_limit_bytes && record_buf_bytes > @max_send_limit_bytes
@@ -1,6 +1,8 @@
1
1
  require 'helper'
2
2
  require 'fluent/test/helpers'
3
- require 'fluent/output'
3
+ require 'fluent/test/driver/input'
4
+ require 'fluent/test/driver/output'
5
+ require 'securerandom'
4
6
 
5
7
  class Kafka2OutputTest < Test::Unit::TestCase
6
8
  include Fluent::Test::Helpers
@@ -15,8 +17,8 @@ class Kafka2OutputTest < Test::Unit::TestCase
15
17
  ])
16
18
  end
17
19
 
18
- def config
19
- base_config + config_element('ROOT', '', {"default_topic" => "kitagawakeiko",
20
+ def config(default_topic: "kitagawakeiko")
21
+ base_config + config_element('ROOT', '', {"default_topic" => default_topic,
20
22
  "brokers" => "localhost:9092"}, [
21
23
  ])
22
24
  end
@@ -57,4 +59,58 @@ class Kafka2OutputTest < Test::Unit::TestCase
57
59
  d = create_driver
58
60
  assert_equal true, d.instance.multi_workers_ready?
59
61
  end
62
+
63
+ class WriteTest < self
64
+ TOPIC_NAME = "kafka-output-#{SecureRandom.uuid}"
65
+
66
+ INPUT_CONFIG = %[
67
+ @type kafka
68
+ brokers localhost:9092
69
+ format json
70
+ @label @kafka
71
+ topics #{TOPIC_NAME}
72
+ ]
73
+
74
+ def create_target_driver(conf = INPUT_CONFIG)
75
+ Fluent::Test::Driver::Input.new(Fluent::KafkaInput).configure(conf)
76
+ end
77
+
78
+ def setup
79
+ @kafka = Kafka.new(["localhost:9092"], client_id: 'kafka')
80
+ end
81
+
82
+ def teardown
83
+ @kafka.delete_topic(TOPIC_NAME)
84
+ @kafka.close
85
+ end
86
+
87
+ def test_write
88
+ target_driver = create_target_driver
89
+ expected_message = {"a" => 2}
90
+ target_driver.run(expect_records: 1, timeout: 5) do
91
+ sleep 2
92
+ d = create_driver(config(default_topic: TOPIC_NAME))
93
+ d.run do
94
+ d.feed("test", event_time, expected_message)
95
+ end
96
+ end
97
+ actual_messages = target_driver.events.collect { |event| event[2] }
98
+ assert_equal([expected_message], actual_messages)
99
+ end
100
+
101
+ def test_exclude_fields
102
+ conf = config(default_topic: TOPIC_NAME) +
103
+ config_element('ROOT', '', {"exclude_fields" => "$.foo"}, [])
104
+ target_driver = create_target_driver
105
+ target_driver.run(expect_records: 1, timeout: 5) do
106
+ sleep 2
107
+ d = create_driver(conf)
108
+ d.run do
109
+ d.feed('test', event_time, {'a' => 'b', 'foo' => 'bar', 'message' => 'test'})
110
+ end
111
+ end
112
+ actual_messages = target_driver.events.collect { |event| event[2] }
113
+ assert_equal([{'a' => 'b', 'message' => 'test'}], actual_messages)
114
+ end
115
+ end
60
116
  end
@@ -0,0 +1,120 @@
1
+ require 'helper'
2
+ require 'fluent/test/helpers'
3
+ require 'fluent/test/driver/input'
4
+ require 'fluent/test/driver/output'
5
+ require 'securerandom'
6
+
7
+ class Rdkafka2OutputTest < Test::Unit::TestCase
8
+ include Fluent::Test::Helpers
9
+
10
+ def have_rdkafka
11
+ begin
12
+ require 'fluent/plugin/out_rdkafka2'
13
+ true
14
+ rescue LoadError
15
+ false
16
+ end
17
+ end
18
+
19
+ def setup
20
+ omit_unless(have_rdkafka, "rdkafka isn't installed")
21
+ Fluent::Test.setup
22
+ end
23
+
24
+ def base_config
25
+ config_element('ROOT', '', {"@type" => "rdkafka2"}, [
26
+ config_element('format', "", {"@type" => "json"})
27
+ ])
28
+ end
29
+
30
+ def config(default_topic: "kitagawakeiko")
31
+ base_config + config_element('ROOT', '', {"default_topic" => default_topic,
32
+ "brokers" => "localhost:9092"}, [
33
+ ])
34
+ end
35
+
36
+ def create_driver(conf = config, tag='test')
37
+ Fluent::Test::Driver::Output.new(Fluent::Rdkafka2Output).configure(conf)
38
+ end
39
+
40
+ def test_configure
41
+ assert_nothing_raised(Fluent::ConfigError) {
42
+ create_driver(base_config)
43
+ }
44
+
45
+ assert_nothing_raised(Fluent::ConfigError) {
46
+ create_driver(config)
47
+ }
48
+
49
+ assert_nothing_raised(Fluent::ConfigError) {
50
+ create_driver(config + config_element('buffer', "", {"@type" => "memory"}))
51
+ }
52
+
53
+ d = create_driver
54
+ assert_equal 'kitagawakeiko', d.instance.default_topic
55
+ assert_equal 'localhost:9092', d.instance.brokers
56
+ end
57
+
58
+ def test_mutli_worker_support
59
+ d = create_driver
60
+ assert_equal true, d.instance.multi_workers_ready?
61
+ end
62
+
63
+ class WriteTest < self
64
+ TOPIC_NAME = "kafka-output-#{SecureRandom.uuid}"
65
+
66
+ INPUT_CONFIG = %[
67
+ @type kafka
68
+ brokers localhost:9092
69
+ format json
70
+ @label @kafka
71
+ topics #{TOPIC_NAME}
72
+ ]
73
+
74
+ def create_target_driver(conf = INPUT_CONFIG)
75
+ Fluent::Test::Driver::Input.new(Fluent::KafkaInput).configure(conf)
76
+ end
77
+
78
+ def setup
79
+ @kafka = nil
80
+ omit_unless(have_rdkafka, "rdkafka isn't installed")
81
+ @kafka = Kafka.new(["localhost:9092"], client_id: 'kafka')
82
+ end
83
+
84
+ def teardown
85
+ if @kafka
86
+ @kafka.delete_topic(TOPIC_NAME)
87
+ @kafka.close
88
+ end
89
+ end
90
+
91
+ def test_write
92
+ target_driver = create_target_driver
93
+ expected_message = {"a" => 2}
94
+ target_driver.run(expect_records: 1, timeout: 5) do
95
+ sleep 2
96
+ d = create_driver(config(default_topic: TOPIC_NAME))
97
+ d.run do
98
+ d.feed("test", event_time, expected_message)
99
+ end
100
+ end
101
+ actual_messages = target_driver.events.collect { |event| event[2] }
102
+ assert_equal([expected_message], actual_messages)
103
+ end
104
+
105
+ def test_exclude_fields
106
+ conf = config(default_topic: TOPIC_NAME) +
107
+ config_element('ROOT', '', {"exclude_fields" => "$.foo"}, [])
108
+ target_driver = create_target_driver
109
+ target_driver.run(expect_records: 1, timeout: 5) do
110
+ sleep 2
111
+ d = create_driver(conf)
112
+ d.run do
113
+ d.feed('test', event_time, {'a' => 'b', 'foo' => 'bar', 'message' => 'test'})
114
+ end
115
+ end
116
+ actual_messages = target_driver.events.collect { |event| event[2] }
117
+ assert_equal([{'a' => 'b', 'message' => 'test'}], actual_messages)
118
+ end
119
+ end
120
+ end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluent-plugin-kafka
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.17.0
4
+ version: 0.17.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hidemasa Togashi
8
8
  - Masahiro Nakagawa
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2021-08-30 00:00:00.000000000 Z
12
+ date: 2021-09-24 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: fluentd
@@ -173,11 +173,12 @@ files:
173
173
  - test/plugin/test_out_kafka.rb
174
174
  - test/plugin/test_out_kafka2.rb
175
175
  - test/plugin/test_out_kafka_buffered.rb
176
+ - test/plugin/test_out_rdkafka2.rb
176
177
  homepage: https://github.com/fluent/fluent-plugin-kafka
177
178
  licenses:
178
179
  - Apache-2.0
179
180
  metadata: {}
180
- post_install_message:
181
+ post_install_message:
181
182
  rdoc_options: []
182
183
  require_paths:
183
184
  - lib
@@ -192,8 +193,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
192
193
  - !ruby/object:Gem::Version
193
194
  version: '0'
194
195
  requirements: []
195
- rubygems_version: 3.1.4
196
- signing_key:
196
+ rubygems_version: 3.2.5
197
+ signing_key:
197
198
  specification_version: 4
198
199
  summary: Fluentd plugin for Apache Kafka > 0.8
199
200
  test_files:
@@ -204,3 +205,4 @@ test_files:
204
205
  - test/plugin/test_out_kafka.rb
205
206
  - test/plugin/test_out_kafka2.rb
206
207
  - test/plugin/test_out_kafka_buffered.rb
208
+ - test/plugin/test_out_rdkafka2.rb