logstash-integration-kafka 10.8.2-java → 10.11.0-java
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 +4 -4
- data/CHANGELOG.md +14 -1
- data/DEVELOPER.md +1 -1
- data/docs/output-kafka.asciidoc +12 -3
- data/lib/logstash/inputs/kafka.rb +20 -27
- data/lib/logstash/outputs/kafka.rb +4 -11
- data/lib/logstash/plugin_mixins/kafka/avro_schema_registry.rb +108 -0
- data/lib/logstash/plugin_mixins/kafka/common.rb +47 -0
- data/logstash-integration-kafka.gemspec +2 -1
- data/spec/integration/inputs/kafka_spec.rb +9 -12
- data/spec/integration/outputs/kafka_spec.rb +21 -1
- data/spec/unit/inputs/kafka_spec.rb +25 -5
- metadata +18 -4
- data/lib/logstash/plugin_mixins/common.rb +0 -107
- data/lib/logstash/plugin_mixins/kafka_support.rb +0 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2833139d65282055a4e924ccfb50b19341a61af69e0834ef318a62f1459f3a16
|
4
|
+
data.tar.gz: fbfd7e6b94e9a74109b44b838df344ed4d7447d56d624f24e642235eb62b00e3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b35d99bf553fbfd3b78f3f7de62b427b18001853573c5160090bebba5dfb78f4d17aa9f879a94a98805f949b1ed76a3fa82e9c1a7b95815f223a5d04104f5bce
|
7
|
+
data.tar.gz: 0e8edf4f07b00443c39ac55523fb5663395ba0ae524dfedc5181a56b5160c2c33fe110e24bccbb060b43c6be0aa2211b50422d041fa1dea59561a26efc79d687
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
## 10.11.0
|
2
|
+
- Feat: added connections_max_idle_ms setting for output [#118](https://github.com/logstash-plugins/logstash-integration-kafka/pull/118)
|
3
|
+
- Refactor: mixins to follow shared mixin module naming
|
4
|
+
|
5
|
+
## 10.10.1
|
6
|
+
- Update CHANGELOG.md [#114](https://api.github.com/repos/logstash-plugins/logstash-integration-kafka/pulls/114)
|
7
|
+
|
8
|
+
## 10.10.0
|
9
|
+
- Added config setting to enable 'zstd' compression in the Kafka output [#112](https://github.com/logstash-plugins/logstash-integration-kafka/pull/112)
|
10
|
+
|
11
|
+
## 10.9.0
|
12
|
+
- Refactor: leverage codec when using schema registry [#106](https://github.com/logstash-plugins/logstash-integration-kafka/pull/106)
|
13
|
+
Previously using `schema_registry_url` parsed the payload as JSON even if `codec => 'plain'` was set, this is no longer the case.
|
14
|
+
|
1
15
|
## 10.8.2
|
2
16
|
- [DOC] Updates description of `enable_auto_commit=false` to clarify that the commit happens after data is fetched AND written to the queue [#90](https://github.com/logstash-plugins/logstash-integration-kafka/pull/90)
|
3
17
|
- Fix: update to Gradle 7 [#104](https://github.com/logstash-plugins/logstash-integration-kafka/pull/104)
|
@@ -82,7 +96,6 @@
|
|
82
96
|
- Fix links in changelog pointing to stand-alone plugin changelogs.
|
83
97
|
- Refactor: scope java_import to plugin class
|
84
98
|
|
85
|
-
|
86
99
|
## 10.0.0
|
87
100
|
- Initial release of the Kafka Integration Plugin, which combines
|
88
101
|
previously-separate Kafka plugins and shared dependencies into a single
|
data/DEVELOPER.md
CHANGED
@@ -62,7 +62,7 @@ See http://kafka.apache.org/documentation.html#producerconfigs for details about
|
|
62
62
|
kafka {
|
63
63
|
topic_id => ... # string (required), The topic to produce the messages to
|
64
64
|
broker_list => ... # string (optional), default: "localhost:9092", This is for bootstrapping and the producer will only use it for getting metadata
|
65
|
-
compression_codec => ... # string (optional), one of ["none", "gzip", "snappy"], default: "none"
|
65
|
+
compression_codec => ... # string (optional), one of ["none", "gzip", "snappy", "lz4", "zstd"], default: "none"
|
66
66
|
compressed_topics => ... # string (optional), default: "", This parameter allows you to set whether compression should be turned on for particular
|
67
67
|
request_required_acks => ... # number (optional), one of [-1, 0, 1], default: 0, This value controls when a produce request is considered completed
|
68
68
|
serializer_class => ... # string, (optional) default: "kafka.serializer.StringEncoder", The serializer class for messages. The default encoder takes a byte[] and returns the same byte[]
|
data/docs/output-kafka.asciidoc
CHANGED
@@ -84,7 +84,8 @@ See the https://kafka.apache.org/{kafka_client_doc}/documentation for more detai
|
|
84
84
|
| <<plugins-{type}s-{plugin}-buffer_memory>> |<<number,number>>|No
|
85
85
|
| <<plugins-{type}s-{plugin}-client_dns_lookup>> |<<string,string>>|No
|
86
86
|
| <<plugins-{type}s-{plugin}-client_id>> |<<string,string>>|No
|
87
|
-
| <<plugins-{type}s-{plugin}-compression_type>> |<<string,string>>, one of `["none", "gzip", "snappy", "lz4"]`|No
|
87
|
+
| <<plugins-{type}s-{plugin}-compression_type>> |<<string,string>>, one of `["none", "gzip", "snappy", "lz4", "zstd"]`|No
|
88
|
+
| <<plugins-{type}s-{plugin}-connections_max_idle_ms>> |<<number,number>>|No
|
88
89
|
| <<plugins-{type}s-{plugin}-jaas_path>> |a valid filesystem path|No
|
89
90
|
| <<plugins-{type}s-{plugin}-kerberos_config>> |a valid filesystem path|No
|
90
91
|
| <<plugins-{type}s-{plugin}-key_serializer>> |<<string,string>>|No
|
@@ -193,11 +194,19 @@ ip/port by allowing a logical application name to be included with the request
|
|
193
194
|
[id="plugins-{type}s-{plugin}-compression_type"]
|
194
195
|
===== `compression_type`
|
195
196
|
|
196
|
-
* Value can be any of: `none`, `gzip`, `snappy`, `lz4`
|
197
|
+
* Value can be any of: `none`, `gzip`, `snappy`, `lz4`, `zstd`
|
197
198
|
* Default value is `"none"`
|
198
199
|
|
199
200
|
The compression type for all data generated by the producer.
|
200
|
-
The default is none (
|
201
|
+
The default is none (meaning no compression). Valid values are none, gzip, snappy, lz4, or zstd.
|
202
|
+
|
203
|
+
[id="plugins-{type}s-{plugin}-connections_max_idle_ms"]
|
204
|
+
===== `connections_max_idle_ms`
|
205
|
+
|
206
|
+
* Value type is <<number,number>>
|
207
|
+
* Default value is `540000` milliseconds (9 minutes).
|
208
|
+
|
209
|
+
Close idle connections after the number of milliseconds specified by this config.
|
201
210
|
|
202
211
|
[id="plugins-{type}s-{plugin}-jaas_path"]
|
203
212
|
===== `jaas_path`
|
@@ -2,12 +2,11 @@ require 'logstash/namespace'
|
|
2
2
|
require 'logstash/inputs/base'
|
3
3
|
require 'stud/interval'
|
4
4
|
require 'java'
|
5
|
-
require 'logstash-integration-kafka_jars.rb'
|
6
|
-
require 'logstash/plugin_mixins/kafka_support'
|
7
|
-
require 'manticore'
|
8
5
|
require "json"
|
9
6
|
require "logstash/json"
|
10
|
-
|
7
|
+
require 'logstash-integration-kafka_jars.rb'
|
8
|
+
require 'logstash/plugin_mixins/kafka/common'
|
9
|
+
require 'logstash/plugin_mixins/kafka/avro_schema_registry'
|
11
10
|
require 'logstash/plugin_mixins/deprecation_logger_support'
|
12
11
|
|
13
12
|
# This input will read events from a Kafka topic. It uses the 0.10 version of
|
@@ -57,13 +56,18 @@ class LogStash::Inputs::Kafka < LogStash::Inputs::Base
|
|
57
56
|
|
58
57
|
DEFAULT_DESERIALIZER_CLASS = "org.apache.kafka.common.serialization.StringDeserializer"
|
59
58
|
|
60
|
-
include LogStash::PluginMixins::
|
61
|
-
include
|
59
|
+
include LogStash::PluginMixins::Kafka::Common
|
60
|
+
include LogStash::PluginMixins::Kafka::AvroSchemaRegistry
|
62
61
|
include LogStash::PluginMixins::DeprecationLoggerSupport
|
63
62
|
|
64
63
|
config_name 'kafka'
|
65
64
|
|
66
|
-
default :codec, 'plain'
|
65
|
+
# default :codec, 'plain' or 'json' depending whether schema registry is used
|
66
|
+
#
|
67
|
+
# @override LogStash::Inputs::Base - removing the `:default => :plain`
|
68
|
+
config :codec, :validate => :codec
|
69
|
+
# NOTE: isn't necessary due the params['codec'] = ... done in #initialize
|
70
|
+
# having the `nil` default explicit makes the behavior more noticeable.
|
67
71
|
|
68
72
|
# The frequency in milliseconds that the consumer offsets are committed to Kafka.
|
69
73
|
config :auto_commit_interval_ms, :validate => :number, :default => 5000 # Kafka default
|
@@ -93,8 +97,6 @@ class LogStash::Inputs::Kafka < LogStash::Inputs::Base
|
|
93
97
|
# is to be able to track the source of requests beyond just ip/port by allowing
|
94
98
|
# a logical application name to be included.
|
95
99
|
config :client_id, :validate => :string, :default => "logstash"
|
96
|
-
# Close idle connections after the number of milliseconds specified by this config.
|
97
|
-
config :connections_max_idle_ms, :validate => :number, :default => 540_000 # (9m) Kafka default
|
98
100
|
# Ideally you should have as many threads as the number of partitions for a perfect
|
99
101
|
# balance — more threads than partitions means that some threads will be idle
|
100
102
|
config :consumer_threads, :validate => :number, :default => 1
|
@@ -147,9 +149,6 @@ class LogStash::Inputs::Kafka < LogStash::Inputs::Base
|
|
147
149
|
config :max_partition_fetch_bytes, :validate => :number, :default => 1_048_576 # (1MB) Kafka default
|
148
150
|
# The maximum number of records returned in a single call to poll().
|
149
151
|
config :max_poll_records, :validate => :number, :default => 500 # Kafka default
|
150
|
-
# The period of time in milliseconds after which we force a refresh of metadata even if
|
151
|
-
# we haven't seen any partition leadership changes to proactively discover any new brokers or partitions
|
152
|
-
config :metadata_max_age_ms, :validate => :number, :default => 300_000 # (5m) Kafka default
|
153
152
|
# The name of the partition assignment strategy that the client uses to distribute
|
154
153
|
# partition ownership amongst consumer instances, supported options are `range`,
|
155
154
|
# `round_robin`, `sticky` and `cooperative_sticky`
|
@@ -162,10 +161,6 @@ class LogStash::Inputs::Kafka < LogStash::Inputs::Base
|
|
162
161
|
# This avoids repeatedly connecting to a host in a tight loop.
|
163
162
|
# This backoff applies to all connection attempts by the client to a broker.
|
164
163
|
config :reconnect_backoff_ms, :validate => :number, :default => 50 # Kafka default
|
165
|
-
# The configuration controls the maximum amount of time the client will wait for the response of a request.
|
166
|
-
# If the response is not received before the timeout elapses the client will resend the request if necessary
|
167
|
-
# or fail the request if retries are exhausted.
|
168
|
-
config :request_timeout_ms, :validate => :number, :default => 40_000 # Kafka default
|
169
164
|
# The amount of time to wait before attempting to retry a failed fetch request
|
170
165
|
# to a given topic partition. This avoids repeated fetching-and-failing in a tight loop.
|
171
166
|
config :retry_backoff_ms, :validate => :number, :default => 100 # Kafka default
|
@@ -249,6 +244,15 @@ class LogStash::Inputs::Kafka < LogStash::Inputs::Base
|
|
249
244
|
|
250
245
|
attr_reader :metadata_mode
|
251
246
|
|
247
|
+
# @overload based on schema registry change the codec default
|
248
|
+
def initialize(params = {})
|
249
|
+
unless params.key?('codec')
|
250
|
+
params['codec'] = params.key?('schema_registry_url') ? 'json' : 'plain'
|
251
|
+
end
|
252
|
+
|
253
|
+
super(params)
|
254
|
+
end
|
255
|
+
|
252
256
|
public
|
253
257
|
def register
|
254
258
|
@runner_threads = []
|
@@ -341,22 +345,11 @@ class LogStash::Inputs::Kafka < LogStash::Inputs::Base
|
|
341
345
|
def handle_record(record, codec_instance, queue)
|
342
346
|
codec_instance.decode(record.value.to_s) do |event|
|
343
347
|
decorate(event)
|
344
|
-
maybe_apply_schema(event, record)
|
345
348
|
maybe_set_metadata(event, record)
|
346
349
|
queue << event
|
347
350
|
end
|
348
351
|
end
|
349
352
|
|
350
|
-
def maybe_apply_schema(event, record)
|
351
|
-
if schema_registry_url
|
352
|
-
json = LogStash::Json.load(record.value.to_s)
|
353
|
-
json.each do |k, v|
|
354
|
-
event.set(k, v)
|
355
|
-
end
|
356
|
-
event.remove("message")
|
357
|
-
end
|
358
|
-
end
|
359
|
-
|
360
353
|
def maybe_set_metadata(event, record)
|
361
354
|
if @metadata_mode.include?(:record_props)
|
362
355
|
event.set("[@metadata][kafka][topic]", record.topic)
|
@@ -2,7 +2,7 @@ require 'logstash/namespace'
|
|
2
2
|
require 'logstash/outputs/base'
|
3
3
|
require 'java'
|
4
4
|
require 'logstash-integration-kafka_jars.rb'
|
5
|
-
require 'logstash/plugin_mixins/
|
5
|
+
require 'logstash/plugin_mixins/kafka/common'
|
6
6
|
|
7
7
|
# Write events to a Kafka topic. This uses the Kafka Producer API to write messages to a topic on
|
8
8
|
# the broker.
|
@@ -51,7 +51,7 @@ class LogStash::Outputs::Kafka < LogStash::Outputs::Base
|
|
51
51
|
|
52
52
|
java_import org.apache.kafka.clients.producer.ProducerRecord
|
53
53
|
|
54
|
-
include LogStash::PluginMixins::
|
54
|
+
include LogStash::PluginMixins::Kafka::Common
|
55
55
|
|
56
56
|
declare_threadsafe!
|
57
57
|
|
@@ -80,8 +80,8 @@ class LogStash::Outputs::Kafka < LogStash::Outputs::Base
|
|
80
80
|
# The total bytes of memory the producer can use to buffer records waiting to be sent to the server.
|
81
81
|
config :buffer_memory, :validate => :number, :default => 33_554_432 # (32M) Kafka default
|
82
82
|
# The compression type for all data generated by the producer.
|
83
|
-
# The default is none (i.e. no compression). Valid values are none, gzip, or
|
84
|
-
config :compression_type, :validate => ["none", "gzip", "snappy", "lz4"], :default => "none"
|
83
|
+
# The default is none (i.e. no compression). Valid values are none, gzip, snappy, lz4 or zstd.
|
84
|
+
config :compression_type, :validate => ["none", "gzip", "snappy", "lz4", "zstd"], :default => "none"
|
85
85
|
# How DNS lookups should be done. If set to `use_all_dns_ips`, when the lookup returns multiple
|
86
86
|
# IP addresses for a hostname, they will all be attempted to connect to before failing the
|
87
87
|
# connection. If the value is `resolve_canonical_bootstrap_servers_only` each entry will be
|
@@ -107,19 +107,12 @@ class LogStash::Outputs::Kafka < LogStash::Outputs::Base
|
|
107
107
|
config :message_key, :validate => :string
|
108
108
|
# the timeout setting for initial metadata request to fetch topic metadata.
|
109
109
|
config :metadata_fetch_timeout_ms, :validate => :number, :default => 60_000
|
110
|
-
# the max time in milliseconds before a metadata refresh is forced.
|
111
|
-
config :metadata_max_age_ms, :validate => :number, :default => 300_000 # (5m) Kafka default
|
112
110
|
# Partitioner to use - can be `default`, `uniform_sticky`, `round_robin` or a fully qualified class name of a custom partitioner.
|
113
111
|
config :partitioner, :validate => :string
|
114
112
|
# The size of the TCP receive buffer to use when reading data
|
115
113
|
config :receive_buffer_bytes, :validate => :number, :default => 32_768 # (32KB) Kafka default
|
116
114
|
# The amount of time to wait before attempting to reconnect to a given host when a connection fails.
|
117
115
|
config :reconnect_backoff_ms, :validate => :number, :default => 50 # Kafka default
|
118
|
-
# The configuration controls the maximum amount of time the client will wait
|
119
|
-
# for the response of a request. If the response is not received before the timeout
|
120
|
-
# elapses the client will resend the request if necessary or fail the request if
|
121
|
-
# retries are exhausted.
|
122
|
-
config :request_timeout_ms, :validate => :number, :default => 40_000 # (40s) Kafka default
|
123
116
|
# The default retry behavior is to retry until successful. To prevent data loss,
|
124
117
|
# the use of this setting is discouraged.
|
125
118
|
#
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'manticore'
|
2
|
+
|
3
|
+
module LogStash module PluginMixins module Kafka
|
4
|
+
module AvroSchemaRegistry
|
5
|
+
|
6
|
+
def self.included(base)
|
7
|
+
base.extend(self)
|
8
|
+
base.setup_schema_registry_config
|
9
|
+
end
|
10
|
+
|
11
|
+
def setup_schema_registry_config
|
12
|
+
# Option to set key to access Schema Registry.
|
13
|
+
config :schema_registry_key, :validate => :string
|
14
|
+
|
15
|
+
# Option to set secret to access Schema Registry.
|
16
|
+
config :schema_registry_secret, :validate => :password
|
17
|
+
|
18
|
+
# Option to set the endpoint of the Schema Registry.
|
19
|
+
# This option permit the usage of Avro Kafka deserializer which retrieve the schema of the Avro message from an
|
20
|
+
# instance of schema registry. If this option has value `value_deserializer_class` nor `topics_pattern` could be valued
|
21
|
+
config :schema_registry_url, :validate => :uri
|
22
|
+
|
23
|
+
# Option to set the proxy of the Schema Registry.
|
24
|
+
# This option permits to define a proxy to be used to reach the schema registry service instance.
|
25
|
+
config :schema_registry_proxy, :validate => :uri
|
26
|
+
|
27
|
+
# Option to skip validating the schema registry during registration. This can be useful when using
|
28
|
+
# certificate based auth
|
29
|
+
config :schema_registry_validation, :validate => ['auto', 'skip'], :default => 'auto'
|
30
|
+
end
|
31
|
+
|
32
|
+
def check_schema_registry_parameters
|
33
|
+
if @schema_registry_url
|
34
|
+
check_for_schema_registry_conflicts
|
35
|
+
@schema_registry_proxy_host, @schema_registry_proxy_port = split_proxy_into_host_and_port(schema_registry_proxy)
|
36
|
+
check_for_key_and_secret
|
37
|
+
check_for_schema_registry_connectivity_and_subjects if schema_registry_validation?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def schema_registry_validation?
|
42
|
+
return false if schema_registry_validation.to_s == 'skip'
|
43
|
+
return false if using_kerberos? # pre-validation doesn't support kerberos
|
44
|
+
|
45
|
+
true
|
46
|
+
end
|
47
|
+
|
48
|
+
def using_kerberos?
|
49
|
+
security_protocol == "SASL_PLAINTEXT" || security_protocol == "SASL_SSL"
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
def check_for_schema_registry_conflicts
|
54
|
+
if @value_deserializer_class != LogStash::Inputs::Kafka::DEFAULT_DESERIALIZER_CLASS
|
55
|
+
raise LogStash::ConfigurationError, 'Option schema_registry_url prohibit the customization of value_deserializer_class'
|
56
|
+
end
|
57
|
+
if @topics_pattern && !@topics_pattern.empty?
|
58
|
+
raise LogStash::ConfigurationError, 'Option schema_registry_url prohibit the customization of topics_pattern'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
def check_for_schema_registry_connectivity_and_subjects
|
64
|
+
options = {}
|
65
|
+
if schema_registry_proxy && !schema_registry_proxy.empty?
|
66
|
+
options[:proxy] = schema_registry_proxy.to_s
|
67
|
+
end
|
68
|
+
if schema_registry_key and !schema_registry_key.empty?
|
69
|
+
options[:auth] = {:user => schema_registry_key, :password => schema_registry_secret.value}
|
70
|
+
end
|
71
|
+
client = Manticore::Client.new(options)
|
72
|
+
begin
|
73
|
+
response = client.get(@schema_registry_url.uri.to_s + '/subjects').body
|
74
|
+
rescue Manticore::ManticoreException => e
|
75
|
+
raise LogStash::ConfigurationError.new("Schema registry service doesn't respond, error: #{e.message}")
|
76
|
+
end
|
77
|
+
registered_subjects = JSON.parse response
|
78
|
+
expected_subjects = @topics.map { |t| "#{t}-value"}
|
79
|
+
if (expected_subjects & registered_subjects).size != expected_subjects.size
|
80
|
+
undefined_topic_subjects = expected_subjects - registered_subjects
|
81
|
+
raise LogStash::ConfigurationError, "The schema registry does not contain definitions for required topic subjects: #{undefined_topic_subjects}"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def split_proxy_into_host_and_port(proxy_uri)
|
86
|
+
return nil unless proxy_uri && !proxy_uri.empty?
|
87
|
+
|
88
|
+
port = proxy_uri.port
|
89
|
+
|
90
|
+
host_spec = ""
|
91
|
+
host_spec << proxy_uri.scheme || "http"
|
92
|
+
host_spec << "://"
|
93
|
+
host_spec << "#{proxy_uri.userinfo}@" if proxy_uri.userinfo
|
94
|
+
host_spec << proxy_uri.host
|
95
|
+
|
96
|
+
[host_spec, port]
|
97
|
+
end
|
98
|
+
|
99
|
+
def check_for_key_and_secret
|
100
|
+
if schema_registry_key and !schema_registry_key.empty?
|
101
|
+
if !schema_registry_secret or schema_registry_secret.value.empty?
|
102
|
+
raise LogStash::ConfigurationError, "Setting `schema_registry_secret` is required when `schema_registry_key` is provided."
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
end end end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module LogStash module PluginMixins module Kafka
|
2
|
+
module Common
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
# COMMON CONFIGURATION SUPPORTED BY BOTH PRODUCER/CONSUMER
|
6
|
+
|
7
|
+
# Close idle connections after the number of milliseconds specified by this config.
|
8
|
+
base.config :connections_max_idle_ms, :validate => :number, :default => 540_000 # (9m) Kafka default
|
9
|
+
|
10
|
+
# The period of time in milliseconds after which we force a refresh of metadata even if
|
11
|
+
# we haven't seen any partition leadership changes to proactively discover any new brokers or partitions
|
12
|
+
base.config :metadata_max_age_ms, :validate => :number, :default => 300_000 # (5m) Kafka default
|
13
|
+
|
14
|
+
# The configuration controls the maximum amount of time the client will wait for the response of a request.
|
15
|
+
# If the response is not received before the timeout elapses the client will resend the request if necessary
|
16
|
+
# or fail the request if retries are exhausted.
|
17
|
+
base.config :request_timeout_ms, :validate => :number, :default => 40_000 # Kafka default
|
18
|
+
end
|
19
|
+
|
20
|
+
def set_trustore_keystore_config(props)
|
21
|
+
props.put("ssl.truststore.type", ssl_truststore_type) unless ssl_truststore_type.nil?
|
22
|
+
props.put("ssl.truststore.location", ssl_truststore_location) unless ssl_truststore_location.nil?
|
23
|
+
props.put("ssl.truststore.password", ssl_truststore_password.value) unless ssl_truststore_password.nil?
|
24
|
+
|
25
|
+
# Client auth stuff
|
26
|
+
props.put("ssl.keystore.type", ssl_keystore_type) unless ssl_keystore_type.nil?
|
27
|
+
props.put("ssl.key.password", ssl_key_password.value) unless ssl_key_password.nil?
|
28
|
+
props.put("ssl.keystore.location", ssl_keystore_location) unless ssl_keystore_location.nil?
|
29
|
+
props.put("ssl.keystore.password", ssl_keystore_password.value) unless ssl_keystore_password.nil?
|
30
|
+
props.put("ssl.endpoint.identification.algorithm", ssl_endpoint_identification_algorithm) unless ssl_endpoint_identification_algorithm.nil?
|
31
|
+
end
|
32
|
+
|
33
|
+
def set_sasl_config(props)
|
34
|
+
java.lang.System.setProperty("java.security.auth.login.config", jaas_path) unless jaas_path.nil?
|
35
|
+
java.lang.System.setProperty("java.security.krb5.conf", kerberos_config) unless kerberos_config.nil?
|
36
|
+
|
37
|
+
props.put("sasl.mechanism", sasl_mechanism)
|
38
|
+
if sasl_mechanism == "GSSAPI" && sasl_kerberos_service_name.nil?
|
39
|
+
raise LogStash::ConfigurationError, "sasl_kerberos_service_name must be specified when SASL mechanism is GSSAPI"
|
40
|
+
end
|
41
|
+
|
42
|
+
props.put("sasl.kerberos.service.name", sasl_kerberos_service_name) unless sasl_kerberos_service_name.nil?
|
43
|
+
props.put("sasl.jaas.config", sasl_jaas_config) unless sasl_jaas_config.nil?
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end end end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'logstash-integration-kafka'
|
3
|
-
s.version = '10.
|
3
|
+
s.version = '10.11.0'
|
4
4
|
s.licenses = ['Apache-2.0']
|
5
5
|
s.summary = "Integration with Kafka - input and output plugins"
|
6
6
|
s.description = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline "+
|
@@ -50,6 +50,7 @@ Gem::Specification.new do |s|
|
|
50
50
|
s.add_runtime_dependency 'logstash-mixin-deprecation_logger_support', '~>1.0'
|
51
51
|
|
52
52
|
s.add_development_dependency 'logstash-devutils'
|
53
|
+
s.add_development_dependency 'logstash-codec-line'
|
53
54
|
s.add_development_dependency 'rspec-wait'
|
54
55
|
s.add_development_dependency 'digest-crc', '~> 0.5.1' # 0.6.0 started using a C-ext
|
55
56
|
s.add_development_dependency 'ruby-kafka' # depends on digest-crc
|
@@ -16,38 +16,38 @@ describe "inputs/kafka", :integration => true do
|
|
16
16
|
let(:group_id_5) {rand(36**8).to_s(36)}
|
17
17
|
let(:group_id_6) {rand(36**8).to_s(36)}
|
18
18
|
let(:plain_config) do
|
19
|
-
{ 'topics' => ['logstash_integration_topic_plain'], '
|
19
|
+
{ 'topics' => ['logstash_integration_topic_plain'], 'group_id' => group_id_1,
|
20
20
|
'auto_offset_reset' => 'earliest' }
|
21
21
|
end
|
22
22
|
let(:multi_consumer_config) do
|
23
23
|
plain_config.merge({"group_id" => group_id_4, "client_id" => "spec", "consumer_threads" => 3})
|
24
24
|
end
|
25
25
|
let(:snappy_config) do
|
26
|
-
{ 'topics' => ['logstash_integration_topic_snappy'], '
|
26
|
+
{ 'topics' => ['logstash_integration_topic_snappy'], 'group_id' => group_id_1,
|
27
27
|
'auto_offset_reset' => 'earliest' }
|
28
28
|
end
|
29
29
|
let(:lz4_config) do
|
30
|
-
{ 'topics' => ['logstash_integration_topic_lz4'], '
|
30
|
+
{ 'topics' => ['logstash_integration_topic_lz4'], 'group_id' => group_id_1,
|
31
31
|
'auto_offset_reset' => 'earliest' }
|
32
32
|
end
|
33
33
|
let(:pattern_config) do
|
34
|
-
{ 'topics_pattern' => 'logstash_integration_topic_.*', 'group_id' => group_id_2,
|
34
|
+
{ 'topics_pattern' => 'logstash_integration_topic_.*', 'group_id' => group_id_2,
|
35
35
|
'auto_offset_reset' => 'earliest' }
|
36
36
|
end
|
37
37
|
let(:decorate_config) do
|
38
|
-
{ 'topics' => ['logstash_integration_topic_plain'], '
|
38
|
+
{ 'topics' => ['logstash_integration_topic_plain'], 'group_id' => group_id_3,
|
39
39
|
'auto_offset_reset' => 'earliest', 'decorate_events' => 'true' }
|
40
40
|
end
|
41
41
|
let(:decorate_headers_config) do
|
42
|
-
{ 'topics' => ['logstash_integration_topic_plain_with_headers'], '
|
42
|
+
{ 'topics' => ['logstash_integration_topic_plain_with_headers'], 'group_id' => group_id_3,
|
43
43
|
'auto_offset_reset' => 'earliest', 'decorate_events' => 'extended' }
|
44
44
|
end
|
45
45
|
let(:decorate_bad_headers_config) do
|
46
|
-
{ 'topics' => ['logstash_integration_topic_plain_with_headers_badly'], '
|
46
|
+
{ 'topics' => ['logstash_integration_topic_plain_with_headers_badly'], 'group_id' => group_id_3,
|
47
47
|
'auto_offset_reset' => 'earliest', 'decorate_events' => 'extended' }
|
48
48
|
end
|
49
49
|
let(:manual_commit_config) do
|
50
|
-
{ 'topics' => ['logstash_integration_topic_plain'], '
|
50
|
+
{ 'topics' => ['logstash_integration_topic_plain'], 'group_id' => group_id_5,
|
51
51
|
'auto_offset_reset' => 'earliest', 'enable_auto_commit' => 'false' }
|
52
52
|
end
|
53
53
|
let(:timeout_seconds) { 30 }
|
@@ -352,10 +352,7 @@ describe "Deserializing with the schema registry", :integration => true do
|
|
352
352
|
|
353
353
|
let(:base_config) do
|
354
354
|
{
|
355
|
-
'topics' => [avro_topic_name],
|
356
|
-
'codec' => 'plain',
|
357
|
-
'group_id' => group_id_1,
|
358
|
-
'auto_offset_reset' => 'earliest'
|
355
|
+
'topics' => [avro_topic_name], 'group_id' => group_id_1, 'auto_offset_reset' => 'earliest'
|
359
356
|
}
|
360
357
|
end
|
361
358
|
|
@@ -28,7 +28,8 @@ describe "outputs/kafka", :integration => true do
|
|
28
28
|
let(:num_events) { 3 }
|
29
29
|
|
30
30
|
before :each do
|
31
|
-
|
31
|
+
# NOTE: the connections_max_idle_ms is irrelevant just testing that configuration works ...
|
32
|
+
config = base_config.merge({"topic_id" => test_topic, "connections_max_idle_ms" => 540_000})
|
32
33
|
load_kafka_data(config)
|
33
34
|
end
|
34
35
|
|
@@ -139,6 +140,25 @@ describe "outputs/kafka", :integration => true do
|
|
139
140
|
# end
|
140
141
|
end
|
141
142
|
|
143
|
+
context 'when using zstd compression' do
|
144
|
+
let(:test_topic) { 'logstash_integration_zstd_topic' }
|
145
|
+
|
146
|
+
before :each do
|
147
|
+
config = base_config.merge({"topic_id" => test_topic, "compression_type" => "zstd"})
|
148
|
+
load_kafka_data(config)
|
149
|
+
end
|
150
|
+
|
151
|
+
# NOTE: depends on zstd-ruby gem which is using a C-extension
|
152
|
+
# it 'should have data integrity' do
|
153
|
+
# messages = fetch_messages(test_topic)
|
154
|
+
#
|
155
|
+
# expect(messages.size).to eq(num_events)
|
156
|
+
# messages.each do |m|
|
157
|
+
# expect(m.value).to eq(event.to_s)
|
158
|
+
# end
|
159
|
+
# end
|
160
|
+
end
|
161
|
+
|
142
162
|
context 'when using multi partition topic' do
|
143
163
|
let(:num_events) { 100 } # ~ more than (batch.size) 16,384 bytes
|
144
164
|
let(:test_topic) { 'logstash_integration_topic3' }
|
@@ -177,7 +177,23 @@ describe LogStash::Inputs::Kafka do
|
|
177
177
|
end
|
178
178
|
end
|
179
179
|
|
180
|
-
|
180
|
+
it 'uses plain codec by default' do
|
181
|
+
expect( subject.codec ).to respond_to :decode
|
182
|
+
expect( subject.codec.class ).to be LogStash::Codecs::Plain
|
183
|
+
end
|
184
|
+
|
185
|
+
context 'with codec option' do
|
186
|
+
|
187
|
+
let(:config) { super().merge 'codec' => 'line' }
|
188
|
+
|
189
|
+
it 'uses specified codec' do
|
190
|
+
expect( subject.codec ).to respond_to :decode
|
191
|
+
expect( subject.codec.class ).to be LogStash::Codecs::Line
|
192
|
+
end
|
193
|
+
|
194
|
+
end
|
195
|
+
|
196
|
+
describe "schema registry" do
|
181
197
|
let(:base_config) do {
|
182
198
|
'schema_registry_url' => 'http://localhost:8081',
|
183
199
|
'topics' => ['logstash'],
|
@@ -186,7 +202,7 @@ describe LogStash::Inputs::Kafka do
|
|
186
202
|
end
|
187
203
|
|
188
204
|
context "schema_registry_url" do
|
189
|
-
|
205
|
+
let(:config) { base_config }
|
190
206
|
|
191
207
|
it "conflict with value_deserializer_class should fail" do
|
192
208
|
config['value_deserializer_class'] = 'my.fantasy.Deserializer'
|
@@ -197,6 +213,11 @@ describe LogStash::Inputs::Kafka do
|
|
197
213
|
config['topics_pattern'] = 'topic_.*'
|
198
214
|
expect { subject.register }.to raise_error LogStash::ConfigurationError, /Option schema_registry_url prohibit the customization of topics_pattern/
|
199
215
|
end
|
216
|
+
|
217
|
+
it 'switches default codec to json' do
|
218
|
+
expect( subject.codec ).to respond_to :decode
|
219
|
+
expect( subject.codec.class ).to be LogStash::Codecs::JSON
|
220
|
+
end
|
200
221
|
end
|
201
222
|
|
202
223
|
context 'when kerberos auth is used' do
|
@@ -204,9 +225,8 @@ describe LogStash::Inputs::Kafka do
|
|
204
225
|
context "with #{protocol}" do
|
205
226
|
['auto', 'skip'].each do |vsr|
|
206
227
|
context "when validata_schema_registry is #{vsr}" do
|
207
|
-
let(:config) { base_config.merge({'security_protocol' => protocol,
|
208
|
-
|
209
|
-
}
|
228
|
+
let(:config) { base_config.merge({'security_protocol' => protocol, 'schema_registry_validation' => vsr}) }
|
229
|
+
|
210
230
|
it 'skips verification' do
|
211
231
|
expect(subject).not_to receive(:check_for_schema_registry_connectivity_and_subjects)
|
212
232
|
expect { subject.register }.not_to raise_error
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: logstash-integration-kafka
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 10.
|
4
|
+
version: 10.11.0
|
5
5
|
platform: java
|
6
6
|
authors:
|
7
7
|
- Elastic
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-04-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
@@ -154,6 +154,20 @@ dependencies:
|
|
154
154
|
- - ">="
|
155
155
|
- !ruby/object:Gem::Version
|
156
156
|
version: '0'
|
157
|
+
- !ruby/object:Gem::Dependency
|
158
|
+
requirement: !ruby/object:Gem::Requirement
|
159
|
+
requirements:
|
160
|
+
- - ">="
|
161
|
+
- !ruby/object:Gem::Version
|
162
|
+
version: '0'
|
163
|
+
name: logstash-codec-line
|
164
|
+
prerelease: false
|
165
|
+
type: :development
|
166
|
+
version_requirements: !ruby/object:Gem::Requirement
|
167
|
+
requirements:
|
168
|
+
- - ">="
|
169
|
+
- !ruby/object:Gem::Version
|
170
|
+
version: '0'
|
157
171
|
- !ruby/object:Gem::Dependency
|
158
172
|
requirement: !ruby/object:Gem::Requirement
|
159
173
|
requirements:
|
@@ -231,8 +245,8 @@ files:
|
|
231
245
|
- lib/logstash-integration-kafka_jars.rb
|
232
246
|
- lib/logstash/inputs/kafka.rb
|
233
247
|
- lib/logstash/outputs/kafka.rb
|
234
|
-
- lib/logstash/plugin_mixins/
|
235
|
-
- lib/logstash/plugin_mixins/
|
248
|
+
- lib/logstash/plugin_mixins/kafka/avro_schema_registry.rb
|
249
|
+
- lib/logstash/plugin_mixins/kafka/common.rb
|
236
250
|
- logstash-integration-kafka.gemspec
|
237
251
|
- spec/check_docs_spec.rb
|
238
252
|
- spec/fixtures/jaas.config
|
@@ -1,107 +0,0 @@
|
|
1
|
-
module LogStash
|
2
|
-
module PluginMixins
|
3
|
-
module KafkaAvroSchemaRegistry
|
4
|
-
|
5
|
-
def self.included(base)
|
6
|
-
base.extend(self)
|
7
|
-
base.setup_schema_registry_config
|
8
|
-
end
|
9
|
-
|
10
|
-
def setup_schema_registry_config
|
11
|
-
# Option to set key to access Schema Registry.
|
12
|
-
config :schema_registry_key, :validate => :string
|
13
|
-
|
14
|
-
# Option to set secret to access Schema Registry.
|
15
|
-
config :schema_registry_secret, :validate => :password
|
16
|
-
|
17
|
-
# Option to set the endpoint of the Schema Registry.
|
18
|
-
# This option permit the usage of Avro Kafka deserializer which retrieve the schema of the Avro message from an
|
19
|
-
# instance of schema registry. If this option has value `value_deserializer_class` nor `topics_pattern` could be valued
|
20
|
-
config :schema_registry_url, :validate => :uri
|
21
|
-
|
22
|
-
# Option to set the proxy of the Schema Registry.
|
23
|
-
# This option permits to define a proxy to be used to reach the schema registry service instance.
|
24
|
-
config :schema_registry_proxy, :validate => :uri
|
25
|
-
|
26
|
-
# Option to skip validating the schema registry during registration. This can be useful when using
|
27
|
-
# certificate based auth
|
28
|
-
config :schema_registry_validation, :validate => ['auto', 'skip'], :default => 'auto'
|
29
|
-
end
|
30
|
-
|
31
|
-
def check_schema_registry_parameters
|
32
|
-
if @schema_registry_url
|
33
|
-
check_for_schema_registry_conflicts
|
34
|
-
@schema_registry_proxy_host, @schema_registry_proxy_port = split_proxy_into_host_and_port(schema_registry_proxy)
|
35
|
-
check_for_key_and_secret
|
36
|
-
check_for_schema_registry_connectivity_and_subjects if schema_registry_validation?
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def schema_registry_validation?
|
41
|
-
return false if schema_registry_validation.to_s == 'skip'
|
42
|
-
return false if using_kerberos? # pre-validation doesn't support kerberos
|
43
|
-
|
44
|
-
true
|
45
|
-
end
|
46
|
-
|
47
|
-
def using_kerberos?
|
48
|
-
security_protocol == "SASL_PLAINTEXT" || security_protocol == "SASL_SSL"
|
49
|
-
end
|
50
|
-
|
51
|
-
private
|
52
|
-
def check_for_schema_registry_conflicts
|
53
|
-
if @value_deserializer_class != LogStash::Inputs::Kafka::DEFAULT_DESERIALIZER_CLASS
|
54
|
-
raise LogStash::ConfigurationError, 'Option schema_registry_url prohibit the customization of value_deserializer_class'
|
55
|
-
end
|
56
|
-
if @topics_pattern && !@topics_pattern.empty?
|
57
|
-
raise LogStash::ConfigurationError, 'Option schema_registry_url prohibit the customization of topics_pattern'
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
private
|
62
|
-
def check_for_schema_registry_connectivity_and_subjects
|
63
|
-
options = {}
|
64
|
-
if schema_registry_proxy && !schema_registry_proxy.empty?
|
65
|
-
options[:proxy] = schema_registry_proxy.to_s
|
66
|
-
end
|
67
|
-
if schema_registry_key and !schema_registry_key.empty?
|
68
|
-
options[:auth] = {:user => schema_registry_key, :password => schema_registry_secret.value}
|
69
|
-
end
|
70
|
-
client = Manticore::Client.new(options)
|
71
|
-
begin
|
72
|
-
response = client.get(@schema_registry_url.uri.to_s + '/subjects').body
|
73
|
-
rescue Manticore::ManticoreException => e
|
74
|
-
raise LogStash::ConfigurationError.new("Schema registry service doesn't respond, error: #{e.message}")
|
75
|
-
end
|
76
|
-
registered_subjects = JSON.parse response
|
77
|
-
expected_subjects = @topics.map { |t| "#{t}-value"}
|
78
|
-
if (expected_subjects & registered_subjects).size != expected_subjects.size
|
79
|
-
undefined_topic_subjects = expected_subjects - registered_subjects
|
80
|
-
raise LogStash::ConfigurationError, "The schema registry does not contain definitions for required topic subjects: #{undefined_topic_subjects}"
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
def split_proxy_into_host_and_port(proxy_uri)
|
85
|
-
return nil unless proxy_uri && !proxy_uri.empty?
|
86
|
-
|
87
|
-
port = proxy_uri.port
|
88
|
-
|
89
|
-
host_spec = ""
|
90
|
-
host_spec << proxy_uri.scheme || "http"
|
91
|
-
host_spec << "://"
|
92
|
-
host_spec << "#{proxy_uri.userinfo}@" if proxy_uri.userinfo
|
93
|
-
host_spec << proxy_uri.host
|
94
|
-
|
95
|
-
[host_spec, port]
|
96
|
-
end
|
97
|
-
|
98
|
-
def check_for_key_and_secret
|
99
|
-
if schema_registry_key and !schema_registry_key.empty?
|
100
|
-
if !schema_registry_secret or schema_registry_secret.value.empty?
|
101
|
-
raise LogStash::ConfigurationError, "Setting `schema_registry_secret` is required when `schema_registry_key` is provided."
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
107
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
module LogStash module PluginMixins module KafkaSupport
|
2
|
-
|
3
|
-
def set_trustore_keystore_config(props)
|
4
|
-
props.put("ssl.truststore.type", ssl_truststore_type) unless ssl_truststore_type.nil?
|
5
|
-
props.put("ssl.truststore.location", ssl_truststore_location) unless ssl_truststore_location.nil?
|
6
|
-
props.put("ssl.truststore.password", ssl_truststore_password.value) unless ssl_truststore_password.nil?
|
7
|
-
|
8
|
-
# Client auth stuff
|
9
|
-
props.put("ssl.keystore.type", ssl_keystore_type) unless ssl_keystore_type.nil?
|
10
|
-
props.put("ssl.key.password", ssl_key_password.value) unless ssl_key_password.nil?
|
11
|
-
props.put("ssl.keystore.location", ssl_keystore_location) unless ssl_keystore_location.nil?
|
12
|
-
props.put("ssl.keystore.password", ssl_keystore_password.value) unless ssl_keystore_password.nil?
|
13
|
-
props.put("ssl.endpoint.identification.algorithm", ssl_endpoint_identification_algorithm) unless ssl_endpoint_identification_algorithm.nil?
|
14
|
-
end
|
15
|
-
|
16
|
-
def set_sasl_config(props)
|
17
|
-
java.lang.System.setProperty("java.security.auth.login.config", jaas_path) unless jaas_path.nil?
|
18
|
-
java.lang.System.setProperty("java.security.krb5.conf", kerberos_config) unless kerberos_config.nil?
|
19
|
-
|
20
|
-
props.put("sasl.mechanism", sasl_mechanism)
|
21
|
-
if sasl_mechanism == "GSSAPI" && sasl_kerberos_service_name.nil?
|
22
|
-
raise LogStash::ConfigurationError, "sasl_kerberos_service_name must be specified when SASL mechanism is GSSAPI"
|
23
|
-
end
|
24
|
-
|
25
|
-
props.put("sasl.kerberos.service.name", sasl_kerberos_service_name) unless sasl_kerberos_service_name.nil?
|
26
|
-
props.put("sasl.jaas.config", sasl_jaas_config) unless sasl_jaas_config.nil?
|
27
|
-
end
|
28
|
-
|
29
|
-
end end end
|