logstash-integration-jdbc 5.0.4 → 5.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c112f66e8224282b05dd0d43c1eb864164629951ebd9f2fdbc5d08514731057f
4
- data.tar.gz: 69ee8e0cf79f4f4be6d4f0aae5b3f95a1fae46e3458c6a840e23722f9bbb8a60
3
+ metadata.gz: 3c88894d9c18905f8b857fb38288708bce49084b62ebbffea3cac41421f33dbb
4
+ data.tar.gz: 32eb5bce9ba161d5c500bebe20bbd036ebeb20ba1ee149247fb8b169be790bf9
5
5
  SHA512:
6
- metadata.gz: 8cc11af1a041fbb7784603be07de4243844bffbdfb980bf0f3ff4394af95d3ea2ed9da8543673f7fa866d999c9ba3df772567b64f31538eb8336cbf68283d39d
7
- data.tar.gz: 9bc94ac7a6575a5ac797a44f8a8d987978a1bd0816733f252d80924e945a89b54383df101b2381b3d95afa2090149b7bdbdac1fb7dce06f34bd3fa987fba3354
6
+ metadata.gz: 2c36ce3aee724b1ec82ee2ce295fd92485acf8ba97ad5428d0d55263cd72a52a9349a523c7d5b5eb76f057fdbf7e8638bd36a7f27ccf1069fa075b0b03e56717
7
+ data.tar.gz: 7441a532d6fbd600239c405220a0efcea6e1eb1aac421287be8b39c2119e907e611f0933e2863aaea3e90e0fb59f095dae9ebbad016fe54563dbcb9808341046
data/CHANGELOG.md CHANGED
@@ -1,3 +1,28 @@
1
+ ## 5.1.1
2
+ - [DOC] Changed docs to indicate that logstash-jdbc-static requires local_table [#56](https://github.com/logstash-plugins/logstash-integration-jdbc/pull/56). Fixes [#55](https://github.com/logstash-plugins/logstash-integration-jdbc/issues/55).
3
+
4
+ ## 5.1.0
5
+ - Added `target` option to JDBC input, allowing the row columns to target a specific field instead of being expanded
6
+ at the root of the event. This allows the input to play nicer with the Elastic Common Schema when
7
+ the input does not follow the schema. [#69](https://github.com/logstash-plugins/logstash-integration-jdbc/issues/69)
8
+
9
+ - Added `target` to JDBC filter static `local_lookups` to verify it's properly valued when ECS is enabled.
10
+ [#71](https://github.com/logstash-plugins/logstash-integration-jdbc/issues/71)
11
+
12
+ ## 5.0.7
13
+ - Feat: try hard to log Java cause (chain) [#62](https://github.com/logstash-plugins/logstash-integration-jdbc/pull/62)
14
+
15
+ This allows seeing a full trace from the JDBC driver in case of connection errors.
16
+
17
+ - Refactored Lookup used in jdbc_streaming and jdbc_static to avoid code duplication. [#59](https://github.com/logstash-plugins/logstash-integration-jdbc/pull/59)
18
+
19
+ ## 5.0.6
20
+ - DOC:Replaced plugin_header file with plugin_header-integration file. [#40](https://github.com/logstash-plugins/logstash-integration-jdbc/pull/40)
21
+
22
+ ## 5.0.5
23
+ - Fixed user sequel_opts not being passed down properly [#37](https://github.com/logstash-plugins/logstash-integration-jdbc/pull/37)
24
+ - Refactored jdbc_streaming to share driver loading, so the fixes from the jdbc plugin also effect jdbc_streaming
25
+
1
26
  ## 5.0.4
2
27
  - Fixed issue where JDBC Drivers that don't correctly register with Java's DriverManager fail to load (such as Sybase) [#34](https://github.com/logstash-plugins/logstash-integration-jdbc/pull/34)
3
28
 
@@ -18,4 +43,4 @@
18
43
  - [JBDC Input version 4.3.19](https://github.com/logstash-plugins/logstash-input-jdbc/blob/v4.3.19/CHANGELOG.md)
19
44
  - [JDBC Static filter version 1.1.0](https://github.com/logstash-plugins/logstash-filter-jdbc_static/blob/v1.1.0/CHANGELOG.md)
20
45
  - [JDBC Streaming filter version 1.0.10](https://github.com/logstash-plugins/logstash-filter-jdbc_streaming/blob/v1.0.10/CHANGELOG.md)
21
-
46
+
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
  Logstash Integration Plugin for JDBC, including Logstash Input and Filter Plugins
3
3
  # Logstash Plugin
4
4
 
5
- [![Travis Build Status](https://travis-ci.org/logstash-plugins/logstash-integration-jdbc.svg)](https://travis-ci.org/logstash-plugins/logstash-integration-jdbc)
5
+ [![Travis Build Status](https://travis-ci.com/logstash-plugins/logstash-integration-jdbc.svg)](https://travis-ci.com/logstash-plugins/logstash-integration-jdbc)
6
6
 
7
7
  This is a plugin for [Logstash](https://github.com/elastic/logstash).
8
8
 
@@ -1,3 +1,4 @@
1
+ :integration: jdbc
1
2
  :plugin: jdbc_static
2
3
  :type: filter
3
4
 
@@ -16,7 +17,7 @@ END - GENERATED VARIABLES, DO NOT EDIT!
16
17
 
17
18
  === Jdbc_static filter plugin
18
19
 
19
- include::{include_path}/plugin_header.asciidoc[]
20
+ include::{include_path}/plugin_header-integration.asciidoc[]
20
21
 
21
22
  ==== Description
22
23
 
@@ -421,7 +422,7 @@ according to the table below.
421
422
  |=======================================================================
422
423
  |Setting |Input type|Required
423
424
  | id|string|No
424
- | table|string|Yes
425
+ | local_table|string|Yes
425
426
  | query|string|Yes
426
427
  | max_rows|number|No
427
428
  | jdbc_connection_string|string|No
@@ -437,7 +438,7 @@ id::
437
438
  An optional identifier. This is used to identify the loader that is
438
439
  generating error messages and log lines.
439
440
 
440
- table::
441
+ local_table::
441
442
  The destination table in the local lookup database that the loader will fill.
442
443
 
443
444
  query::
@@ -1,3 +1,4 @@
1
+ :integration: jdbc
1
2
  :plugin: jdbc_streaming
2
3
  :type: filter
3
4
 
@@ -16,7 +17,7 @@ END - GENERATED VARIABLES, DO NOT EDIT!
16
17
 
17
18
  === Jdbc_streaming filter plugin
18
19
 
19
- include::{include_path}/plugin_header.asciidoc[]
20
+ include::{include_path}/plugin_header-integration.asciidoc[]
20
21
 
21
22
  ==== Description
22
23
 
@@ -1,3 +1,4 @@
1
+ :integration: jdbc
1
2
  :plugin: jdbc
2
3
  :type: input
3
4
  :default_codec: plain
@@ -17,7 +18,7 @@ END - GENERATED VARIABLES, DO NOT EDIT!
17
18
 
18
19
  === Jdbc input plugin
19
20
 
20
- include::{include_path}/plugin_header.asciidoc[]
21
+ include::{include_path}/plugin_header-integration.asciidoc[]
21
22
 
22
23
  ==== Description
23
24
 
@@ -210,6 +211,7 @@ This plugin supports the following configuration options plus the <<plugins-{typ
210
211
  | <<plugins-{type}s-{plugin}-sql_log_level>> |<<string,string>>, one of `["fatal", "error", "warn", "info", "debug"]`|No
211
212
  | <<plugins-{type}s-{plugin}-statement>> |<<string,string>>|No
212
213
  | <<plugins-{type}s-{plugin}-statement_filepath>> |a valid filesystem path|No
214
+ | <<plugins-{type}s-{plugin}-target>> | {logstash-ref}/field-references-deepdive.html[field reference] | No
213
215
  | <<plugins-{type}s-{plugin}-tracking_column>> |<<string,string>>|No
214
216
  | <<plugins-{type}s-{plugin}-tracking_column_type>> |<<string,string>>, one of `["numeric", "timestamp"]`|No
215
217
  | <<plugins-{type}s-{plugin}-use_column_value>> |<<boolean,boolean>>|No
@@ -534,6 +536,17 @@ with the `parameters` setting.
534
536
 
535
537
  Path of file containing statement to execute
536
538
 
539
+ [id="plugins-{type}s-{plugin}-target"]
540
+ ===== `target`
541
+
542
+ * Value type is {logstash-ref}/field-references-deepdive.html[field reference]
543
+ * There is no default value for this setting.
544
+
545
+ Without a `target`, events are created from each row column at the root level.
546
+ When the `target` is set to a field reference, the column of each row is placed in the target field instead.
547
+
548
+ This option can be useful to avoid populating unknown fields when a downstream schema such as ECS is enforced.
549
+
537
550
  [id="plugins-{type}s-{plugin}-tracking_column"]
538
551
  ===== `tracking_column`
539
552
 
@@ -56,6 +56,12 @@ module LogStash module Filters module Jdbc
56
56
  @target = options["target"]
57
57
  @id_used_as_target = @target.nil?
58
58
  if @id_used_as_target
59
+ # target shouldn't be nil if ecs_compatibility is not :disabled
60
+ if globals[:ecs_compatibility] != :disabled
61
+ logger.info('ECS compatibility is enabled but no ``target`` option was specified, it is recommended'\
62
+ ' to set the option to avoid potential schema conflicts (if your data is ECS compliant or'\
63
+ ' non-conflicting feel free to ignore this message)')
64
+ end
59
65
  @target = @id
60
66
  end
61
67
  @options = options
@@ -66,6 +72,7 @@ module LogStash module Filters module Jdbc
66
72
  @prepared_statement = nil
67
73
  @symbol_parameters = nil
68
74
  parse_options
75
+ @load_method_ref = method(:load_data_from_local)
69
76
  end
70
77
 
71
78
  def id_used_as_target?
@@ -81,11 +88,7 @@ module LogStash module Filters module Jdbc
81
88
  end
82
89
 
83
90
  def enhance(local, event)
84
- if @prepared_statement
85
- result = call_prepared(local, event)
86
- else
87
- result = fetch(local, event) # should return a LookupResult
88
- end
91
+ result = retrieve_local_data(local, event, &@load_method_ref) # return a LookupResult
89
92
  if result.failed? || result.parameters_invalid?
90
93
  tag_failure(event)
91
94
  end
@@ -112,6 +115,7 @@ module LogStash module Filters module Jdbc
112
115
  @prepared_parameters.each_with_index { |v, i| hash[:"$p#{i}"] = v }
113
116
  @prepared_param_placeholder_map = hash
114
117
  @prepared_statement = local.prepare(query, hash.keys)
118
+ @load_method_ref = method(:load_data_from_prepared)
115
119
  end
116
120
 
117
121
  private
@@ -128,34 +132,23 @@ module LogStash module Filters module Jdbc
128
132
  end
129
133
  end
130
134
 
131
- def fetch(local, event)
132
- result = LookupResult.new()
133
- if @parameters_specified
134
- params = prepare_parameters_from_event(event, result)
135
- if result.parameters_invalid?
136
- logger.warn? && logger.warn("Parameter field not found in event", :lookup_id => @id, :invalid_parameters => result.invalid_parameters)
137
- return result
138
- end
139
- else
140
- params = {}
135
+ def load_data_from_local(local, query, params, result)
136
+ local.fetch(query, params).each do |row|
137
+ stringified = row.inject({}){|hash,(k,v)| hash[k.to_s] = v; hash} #Stringify row keys
138
+ result.push(stringified)
141
139
  end
142
- begin
143
- logger.debug? && logger.debug("Executing Jdbc query", :lookup_id => @id, :statement => query, :parameters => params)
144
- local.fetch(query, params).each do |row|
145
- stringified = row.inject({}){|hash,(k,v)| hash[k.to_s] = v; hash} #Stringify row keys
146
- result.push(stringified)
147
- end
148
- rescue ::Sequel::Error => e
149
- # all sequel errors are a subclass of this, let all other standard or runtime errors bubble up
150
- result.failed!
151
- logger.warn? && logger.warn("Exception when executing Jdbc query", :lookup_id => @id, :exception => e.message, :backtrace => e.backtrace.take(8))
140
+ end
141
+
142
+ def load_data_from_prepared(_local, _query, params, result)
143
+ @prepared_statement.call(params).each do |row|
144
+ stringified = row.inject({}){|hash,(k,v)| hash[k.to_s] = v; hash} #Stringify row keys
145
+ result.push(stringified)
152
146
  end
153
- # if either of: no records or a Sequel exception occurs the payload is
154
- # empty and the default can be substituted later.
155
- result
156
147
  end
157
148
 
158
- def call_prepared(local, event)
149
+ # the &block is invoked with 4 arguments: local, query[String], params[Hash], result[LookupResult]
150
+ # the result is used as accumulator return variable
151
+ def retrieve_local_data(local, event, &proc)
159
152
  result = LookupResult.new()
160
153
  if @parameters_specified
161
154
  params = prepare_parameters_from_event(event, result)
@@ -168,10 +161,7 @@ module LogStash module Filters module Jdbc
168
161
  end
169
162
  begin
170
163
  logger.debug? && logger.debug("Executing Jdbc query", :lookup_id => @id, :statement => query, :parameters => params)
171
- @prepared_statement.call(params).each do |row|
172
- stringified = row.inject({}){|hash,(k,v)| hash[k.to_s] = v; hash} #Stringify row keys
173
- result.push(stringified)
174
- end
164
+ proc.call(local, query, params, result)
175
165
  rescue ::Sequel::Error => e
176
166
  # all sequel errors are a subclass of this, let all other standard or runtime errors bubble up
177
167
  result.failed!
@@ -2,6 +2,7 @@
2
2
  require "logstash-integration-jdbc_jars"
3
3
  require "logstash/filters/base"
4
4
  require "logstash/namespace"
5
+ require "logstash/plugin_mixins/ecs_compatibility_support"
5
6
  require_relative "jdbc/loader"
6
7
  require_relative "jdbc/loader_schedule"
7
8
  require_relative "jdbc/repeating_load_runner"
@@ -14,6 +15,9 @@ require_relative "jdbc/lookup_processor"
14
15
 
15
16
  #
16
17
  module LogStash module Filters class JdbcStatic < LogStash::Filters::Base
18
+ # adds ecs_compatibility config which could be :disabled or :v1
19
+ include LogStash::PluginMixins::ECSCompatibilitySupport(:disabled, :v1, :v8 => :v1)
20
+
17
21
  config_name "jdbc_static"
18
22
 
19
23
  # Define the loaders, an Array of Hashes, to fetch remote data and create local tables.
@@ -214,6 +218,7 @@ module LogStash module Filters class JdbcStatic < LogStash::Filters::Base
214
218
  options["lookup_jdbc_driver_class"] = @lookup_jdbc_driver_class
215
219
  options["lookup_jdbc_driver_library"] = @lookup_jdbc_driver_library
216
220
  options["lookup_jdbc_connection_string"] = @lookup_jdbc_connection_string
221
+ options["ecs_compatibility"] = ecs_compatibility
217
222
  options
218
223
  end
219
224
 
@@ -1,6 +1,7 @@
1
1
  # encoding: utf-8
2
2
  require "logstash/filters/base"
3
3
  require "logstash/namespace"
4
+ require "logstash/plugin_mixins/jdbc/common"
4
5
  require "logstash/plugin_mixins/jdbc_streaming"
5
6
  require "logstash/plugin_mixins/jdbc_streaming/cache_payload"
6
7
  require "logstash/plugin_mixins/jdbc_streaming/statement_handler"
@@ -46,6 +47,7 @@ require "lru_redux"
46
47
  # }
47
48
  #
48
49
  module LogStash module Filters class JdbcStreaming < LogStash::Filters::Base
50
+ include LogStash::PluginMixins::Jdbc::Common
49
51
  include LogStash::PluginMixins::JdbcStreaming
50
52
 
51
53
  config_name "jdbc_streaming"
@@ -1,7 +1,10 @@
1
1
  # encoding: utf-8
2
2
  require "logstash/inputs/base"
3
3
  require "logstash/namespace"
4
+ require "logstash/plugin_mixins/jdbc/common"
4
5
  require "logstash/plugin_mixins/jdbc/jdbc"
6
+ require "logstash/plugin_mixins/ecs_compatibility_support"
7
+ require "logstash/plugin_mixins/validator_support/field_reference_validation_adapter"
5
8
 
6
9
  # this require_relative returns early unless the JRuby version is between 9.2.0.0 and 9.2.8.0
7
10
  require_relative "tzinfo_jruby_patch"
@@ -126,7 +129,13 @@ require_relative "tzinfo_jruby_patch"
126
129
  # ---------------------------------------------------------------------------------------------------
127
130
  #
128
131
  module LogStash module Inputs class Jdbc < LogStash::Inputs::Base
132
+ include LogStash::PluginMixins::Jdbc::Common
129
133
  include LogStash::PluginMixins::Jdbc::Jdbc
134
+ # adds ecs_compatibility config which could be :disabled or :v1
135
+ include LogStash::PluginMixins::ECSCompatibilitySupport(:disabled,:v1,:v8 => :v1)
136
+ # adds :field_reference validator adapter
137
+ extend LogStash::PluginMixins::ValidatorSupport::FieldReferenceValidationAdapter
138
+
130
139
  config_name "jdbc"
131
140
 
132
141
  # If undefined, Logstash will complain, even if codec is unused.
@@ -207,6 +216,9 @@ module LogStash module Inputs class Jdbc < LogStash::Inputs::Base
207
216
 
208
217
  config :prepared_statement_bind_values, :validate => :array, :default => []
209
218
 
219
+ # Define the target field to store the loaded columns
220
+ config :target, :validate => :field_reference, :required => false
221
+
210
222
  attr_reader :database # for test mocking/stubbing
211
223
 
212
224
  public
@@ -258,6 +270,13 @@ module LogStash module Inputs class Jdbc < LogStash::Inputs::Base
258
270
  converters[encoding] = converter
259
271
  end
260
272
  end
273
+
274
+ # target must be populated if ecs_compatibility is not :disabled
275
+ if @target.nil? && ecs_compatibility != :disabled
276
+ logger.info('ECS compatibility is enabled but no ``target`` option was specified, it is recommended'\
277
+ ' to set the option to avoid potential schema conflicts (if your data is ECS compliant or'\
278
+ ' non-conflicting feel free to ignore this message)')
279
+ end
261
280
  end # def register
262
281
 
263
282
  # test injection points
@@ -316,7 +335,12 @@ module LogStash module Inputs class Jdbc < LogStash::Inputs::Base
316
335
  ## do the necessary conversions to string elements
317
336
  row = Hash[row.map { |k, v| [k.to_s, convert(k, v)] }]
318
337
  end
319
- event = LogStash::Event.new(row)
338
+ if @target
339
+ event = LogStash::Event.new
340
+ event.set(@target, row)
341
+ else
342
+ event = LogStash::Event.new(row)
343
+ end
320
344
  decorate(event)
321
345
  queue << event
322
346
  end
@@ -0,0 +1,66 @@
1
+
2
+ module LogStash module PluginMixins module Jdbc
3
+ module Common
4
+
5
+ private
6
+
7
+ def complete_sequel_opts(defaults = {})
8
+ sequel_opts = @sequel_opts.
9
+ map { |key,val| [key.is_a?(String) ? key.to_sym : key, val] }.
10
+ map { |key,val| [key, val.eql?('true') ? true : (val.eql?('false') ? false : val)] }
11
+ sequel_opts = defaults.merge Hash[sequel_opts]
12
+ sequel_opts[:user] = @jdbc_user unless @jdbc_user.nil? || @jdbc_user.empty?
13
+ sequel_opts[:password] = @jdbc_password.value unless @jdbc_password.nil?
14
+ sequel_opts[:driver] = @driver_impl # Sequel uses this as a fallback, if given URI doesn't auto-load the driver correctly
15
+ sequel_opts
16
+ end
17
+
18
+ def load_driver
19
+ return @driver_impl if @driver_impl ||= nil
20
+
21
+ require "java"
22
+ require "sequel"
23
+ require "sequel/adapters/jdbc"
24
+
25
+ load_driver_jars
26
+ begin
27
+ @driver_impl = Sequel::JDBC.load_driver(@jdbc_driver_class)
28
+ rescue Sequel::AdapterNotFound => e # Sequel::AdapterNotFound, "#{@jdbc_driver_class} not loaded"
29
+ # fix this !!!
30
+ message = if jdbc_driver_library_set?
31
+ "Are you sure you've included the correct jdbc driver in :jdbc_driver_library?"
32
+ else
33
+ ":jdbc_driver_library is not set, are you sure you included " +
34
+ "the proper driver client libraries in your classpath?"
35
+ end
36
+ raise LogStash::PluginLoadingError, "#{e}. #{message}"
37
+ end
38
+ end
39
+
40
+ def load_driver_jars
41
+ if jdbc_driver_library_set?
42
+ @jdbc_driver_library.split(",").each do |driver_jar|
43
+ @logger.debug("loading #{driver_jar}")
44
+ # load 'driver.jar' is different than load 'some.rb' as it only causes the file to be added to
45
+ # JRuby's class-loader lookup (class) path - won't raise a LoadError when file is not readable
46
+ unless FileTest.readable?(driver_jar)
47
+ raise LogStash::PluginLoadingError, "unable to load #{driver_jar} from :jdbc_driver_library, " +
48
+ "file not readable (please check user and group permissions for the path)"
49
+ end
50
+ begin
51
+ require driver_jar
52
+ rescue LoadError => e
53
+ raise LogStash::PluginLoadingError, "unable to load #{driver_jar} from :jdbc_driver_library, #{e.message}"
54
+ rescue StandardError => e
55
+ raise LogStash::PluginLoadingError, "unable to load #{driver_jar} from :jdbc_driver_library, #{e}"
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ def jdbc_driver_library_set?
62
+ !@jdbc_driver_library.nil? && !@jdbc_driver_library.empty?
63
+ end
64
+
65
+ end
66
+ end end end
@@ -106,18 +106,12 @@ module LogStash module PluginMixins module Jdbc
106
106
 
107
107
  private
108
108
  def jdbc_connect
109
- opts = {
110
- :user => @jdbc_user,
111
- :password => @jdbc_password.nil? ? nil : @jdbc_password.value,
112
- :pool_timeout => @jdbc_pool_timeout,
113
- :driver => @driver_impl, # Sequel uses this as a fallback, if given URI doesn't auto-load the driver correctly
114
- :keep_reference => false
115
- }.merge(@sequel_opts)
109
+ sequel_opts = complete_sequel_opts(:pool_timeout => @jdbc_pool_timeout, :keep_reference => false)
116
110
  retry_attempts = @connection_retry_attempts
117
111
  loop do
118
112
  retry_attempts -= 1
119
113
  begin
120
- return Sequel.connect(@jdbc_connection_string, opts)
114
+ return Sequel.connect(@jdbc_connection_string, sequel_opts)
121
115
  rescue Sequel::PoolTimeout => e
122
116
  if retry_attempts <= 0
123
117
  @logger.error("Failed to connect to database. #{@jdbc_pool_timeout} second timeout exceeded. Tried #{@connection_retry_attempts} times.")
@@ -128,64 +122,30 @@ module LogStash module PluginMixins module Jdbc
128
122
  # rescue Java::JavaSql::SQLException, ::Sequel::Error => e
129
123
  rescue ::Sequel::Error => e
130
124
  if retry_attempts <= 0
131
- @logger.error("Unable to connect to database. Tried #{@connection_retry_attempts} times", :error_message => e.message, )
125
+ log_java_exception(e.cause)
126
+ @logger.error("Unable to connect to database. Tried #{@connection_retry_attempts} times", error_details(e, trace: true))
132
127
  raise e
133
128
  else
134
- @logger.error("Unable to connect to database. Trying again", :error_message => e.message)
129
+ @logger.error("Unable to connect to database. Trying again", error_details(e, trace: false))
135
130
  end
136
131
  end
137
132
  sleep(@connection_retry_attempts_wait_time)
138
133
  end
139
134
  end
140
135
 
141
- private
142
-
143
- def load_driver
144
- if @drivers_loaded.false?
145
- require "java"
146
- require "sequel"
147
- require "sequel/adapters/jdbc"
148
-
149
- load_driver_jars
150
- begin
151
- @driver_impl = Sequel::JDBC.load_driver(@jdbc_driver_class)
152
- rescue Sequel::AdapterNotFound => e # Sequel::AdapterNotFound, "#{@jdbc_driver_class} not loaded"
153
- # fix this !!!
154
- message = if jdbc_driver_library_set?
155
- "Are you sure you've included the correct jdbc driver in :jdbc_driver_library?"
156
- else
157
- ":jdbc_driver_library is not set, are you sure you included " +
158
- "the proper driver client libraries in your classpath?"
159
- end
160
- raise LogStash::PluginLoadingError, "#{e}. #{message}"
161
- end
162
- @drivers_loaded.make_true
163
- end
164
- end
165
-
166
- def load_driver_jars
167
- if jdbc_driver_library_set?
168
- @jdbc_driver_library.split(",").each do |driver_jar|
169
- @logger.debug("loading #{driver_jar}")
170
- # load 'driver.jar' is different than load 'some.rb' as it only causes the file to be added to
171
- # JRuby's class-loader lookup (class) path - won't raise a LoadError when file is not readable
172
- unless FileTest.readable?(driver_jar)
173
- raise LogStash::PluginLoadingError, "unable to load #{driver_jar} from :jdbc_driver_library, " +
174
- "file not readable (please check user and group permissions for the path)"
175
- end
176
- begin
177
- require driver_jar
178
- rescue LoadError => e
179
- raise LogStash::PluginLoadingError, "unable to load #{driver_jar} from :jdbc_driver_library, #{e.message}"
180
- rescue StandardError => e
181
- raise LogStash::PluginLoadingError, "unable to load #{driver_jar} from :jdbc_driver_library, #{e}"
182
- end
183
- end
184
- end
136
+ def error_details(e, trace: false)
137
+ details = { :message => e.message, :exception => e.class }
138
+ details[:cause] = e.cause if e.cause
139
+ details[:backtrace] = e.backtrace if trace || @logger.debug?
140
+ details
185
141
  end
186
142
 
187
- def jdbc_driver_library_set?
188
- !@jdbc_driver_library.nil? && !@jdbc_driver_library.empty?
143
+ def log_java_exception(e)
144
+ return unless e.is_a?(java.lang.Exception)
145
+ # @logger.name using the same convention as LS does
146
+ logger = self.class.name.gsub('::', '.').downcase
147
+ logger = org.apache.logging.log4j.LogManager.getLogger(logger)
148
+ logger.error('', e) # prints nested causes
189
149
  end
190
150
 
191
151
  def open_jdbc_connection
@@ -230,7 +190,6 @@ module LogStash module PluginMixins module Jdbc
230
190
  public
231
191
  def prepare_jdbc_connection
232
192
  @connection_lock = ReentrantLock.new
233
- @drivers_loaded = Concurrent::AtomicBoolean.new
234
193
  end
235
194
 
236
195
  public
@@ -55,37 +55,11 @@ module LogStash module PluginMixins module JdbcStreaming
55
55
  config :jdbc_validation_timeout, :validate => :number, :default => 3600
56
56
  end
57
57
 
58
- private
59
-
60
- def load_driver_jars
61
- unless @jdbc_driver_library.nil? || @jdbc_driver_library.empty?
62
- @jdbc_driver_library.split(",").each do |driver_jar|
63
- begin
64
- @logger.debug("loading #{driver_jar}")
65
- # Use https://github.com/jruby/jruby/wiki/CallingJavaFromJRuby#from-jar-files to make classes from jar
66
- # available
67
- require driver_jar
68
- rescue LoadError => e
69
- raise LogStash::PluginLoadingError, "unable to load #{driver_jar} from :jdbc_driver_library, #{e.message}"
70
- end
71
- end
72
- end
73
- end
74
-
75
58
  public
76
59
  def prepare_jdbc_connection
77
- require "sequel"
78
- require "sequel/adapters/jdbc"
79
- require "java"
80
-
81
- load_driver_jars
82
-
83
- @sequel_opts_symbols = @sequel_opts.inject({}) {|hash, (k,v)| hash[k.to_sym] = v; hash}
84
- @sequel_opts_symbols[:user] = @jdbc_user unless @jdbc_user.nil? || @jdbc_user.empty?
85
- @sequel_opts_symbols[:password] = @jdbc_password.value unless @jdbc_password.nil?
60
+ load_driver
86
61
 
87
- @sequel_opts_symbols[:driver] = Sequel::JDBC.load_driver(@jdbc_driver_class)
88
- @database = Sequel.connect(@jdbc_connection_string, @sequel_opts_symbols)
62
+ @database = Sequel.connect(@jdbc_connection_string, complete_sequel_opts)
89
63
  if @jdbc_validate_connection
90
64
  @database.extension(:connection_validator)
91
65
  @database.pool.connection_validation_timeout = @jdbc_validation_timeout
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'logstash-integration-jdbc'
3
- s.version = '5.0.4'
3
+ s.version = '5.1.1'
4
4
  s.licenses = ['Apache License (2.0)']
5
5
  s.summary = "Integration with JDBC - input and filter plugins"
6
6
  s.description = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program"
@@ -36,6 +36,8 @@ Gem::Specification.new do |s|
36
36
  s.add_runtime_dependency 'tzinfo-data'
37
37
  # 3.5 limitation is required for jdbc-static loading schedule
38
38
  s.add_runtime_dependency 'rufus-scheduler', '< 3.5'
39
+ s.add_runtime_dependency 'logstash-mixin-ecs_compatibility_support', '~>1.2'
40
+ s.add_runtime_dependency "logstash-mixin-validator_support", '~> 1.0'
39
41
 
40
42
  s.add_development_dependency "childprocess"
41
43
  s.add_development_dependency 'logstash-devutils'
@@ -59,7 +59,7 @@ module LogStash module Filters
59
59
 
60
60
  let(:plugin) { JdbcStatic.new(settings) }
61
61
 
62
- let(:event) { ::LogStash::Event.new("message" => "some text", "ip" => ipaddr) }
62
+ let(:event) { ::LogStash::Event.new("message" => "some text", "ip" => ipaddr) }
63
63
 
64
64
  let(:ipaddr) { ".3.1.1" }
65
65
 
@@ -248,6 +248,37 @@ module LogStash module Filters module Jdbc
248
248
  expect(subject.valid?).to be_falsey
249
249
  end
250
250
  end
251
+
252
+ describe "validation of target option" do
253
+ let(:lookup_hash) do
254
+ {
255
+ "query" => "select * from servers WHERE ip LIKE ? AND os LIKE ?",
256
+ "prepared_parameters" => ["%%{[ip]}"],
257
+ }
258
+ end
259
+
260
+ it "should log a warn when ECS is enabled and target not defined" do
261
+
262
+ class LoggableLookup < Lookup
263
+
264
+ @@TEST_LOGGER = nil
265
+
266
+ def self.logger=(log)
267
+ @@TEST_LOGGER = log
268
+ end
269
+
270
+ def self.logger
271
+ @@TEST_LOGGER
272
+ end
273
+ end
274
+
275
+ spy_logger = double("logger")
276
+ expect(spy_logger).to receive(:info).once.with(/ECS compatibility is enabled but no .*?target.*? was specified/)
277
+ LoggableLookup.logger = spy_logger
278
+
279
+ LoggableLookup.new(lookup_hash, {:ecs_compatibility => 'v1'}, "lookup-1")
280
+ end
281
+ end
251
282
  end
252
283
  end end end
253
284
 
@@ -45,7 +45,7 @@ describe LogStash::Inputs::Jdbc, :integration => true do
45
45
 
46
46
  context "when supplying a non-existent library" do
47
47
  let(:settings) do
48
- super.merge(
48
+ super().merge(
49
49
  "jdbc_driver_library" => "/no/path/to/postgresql.jar"
50
50
  )
51
51
  end
@@ -61,13 +61,29 @@ describe LogStash::Inputs::Jdbc, :integration => true do
61
61
 
62
62
  context "when connecting to a non-existent server" do
63
63
  let(:settings) do
64
- super.merge(
64
+ super().merge(
65
65
  "jdbc_connection_string" => "jdbc:postgresql://localhost:65000/somedb"
66
66
  )
67
67
  end
68
68
 
69
69
  it "should not register correctly" do
70
70
  plugin.register
71
+ allow( plugin ).to receive(:log_java_exception)
72
+ q = Queue.new
73
+ expect do
74
+ plugin.run(q)
75
+ end.to raise_error(::Sequel::DatabaseConnectionError)
76
+ end
77
+
78
+ it "should log (native) Java driver error" do
79
+ plugin.register
80
+ expect( org.apache.logging.log4j.LogManager ).to receive(:getLogger).and_wrap_original do |m, *args|
81
+ logger = m.call(*args)
82
+ expect( logger ).to receive(:error) do |_, e|
83
+ expect( e ).to be_a org.postgresql.util.PSQLException
84
+ end.and_call_original
85
+ logger
86
+ end
71
87
  q = Queue.new
72
88
  expect do
73
89
  plugin.run(q)
@@ -13,9 +13,13 @@ require "date"
13
13
  # We do not need to set TZ env var anymore because we can have 'Sequel.application_timezone' set to utc by default now.
14
14
 
15
15
  describe LogStash::Inputs::Jdbc do
16
+ let(:connection_string) { "jdbc:derby:memory:testdb;create=true" }
16
17
  let(:mixin_settings) do
17
- { "jdbc_user" => ENV['USER'], "jdbc_driver_class" => "org.apache.derby.jdbc.EmbeddedDriver",
18
- "jdbc_connection_string" => "jdbc:derby:memory:testdb;create=true"}
18
+ {
19
+ "jdbc_user" => ENV['USER'],
20
+ "jdbc_driver_class" => "org.apache.derby.jdbc.EmbeddedDriver",
21
+ "jdbc_connection_string" => connection_string
22
+ }
19
23
  end
20
24
  let(:settings) { {} }
21
25
  let(:plugin) { LogStash::Inputs::Jdbc.new(mixin_settings.merge(settings)) }
@@ -112,6 +116,57 @@ describe LogStash::Inputs::Jdbc do
112
116
  end
113
117
  end
114
118
 
119
+ context "when sequel opts has user option" do
120
+ let(:settings) do
121
+ {
122
+ "jdbc_user" => 'system',
123
+ "statement" => "SELECT * from test_table",
124
+ "sequel_opts" => { "user" => 'from-opts' }
125
+ }
126
+ end
127
+
128
+ before do
129
+ plugin.register
130
+ end
131
+
132
+ after do
133
+ plugin.stop
134
+ end
135
+
136
+ it "should honor set jdbc-user when connecting" do
137
+ expect( Sequel ).to receive(:connect).with connection_string, hash_including(:user=>"system")
138
+ plugin.send(:jdbc_connect)
139
+ end
140
+ end
141
+
142
+ context "with sequel opts" do
143
+ let(:settings) do
144
+ {
145
+ "jdbc_user" => 'system',
146
+ "statement" => "SELECT * from test_table",
147
+ "sequel_opts" => {
148
+ "truthy" => 'true',
149
+ "falsey" => 'false',
150
+ "foo" => 'bar',
151
+ "jdbc_properties" => { "some" => 'true' }
152
+ }
153
+ }
154
+ end
155
+
156
+ before do
157
+ plugin.register
158
+ end
159
+
160
+ after do
161
+ plugin.stop
162
+ end
163
+
164
+ it "should symbolize keys" do
165
+ expect( Sequel ).to receive(:connect).with connection_string,
166
+ hash_including(:truthy => true, :falsey => false, :foo => 'bar', :jdbc_properties => { 'some' => 'true' })
167
+ plugin.send(:jdbc_connect)
168
+ end
169
+ end
115
170
 
116
171
  context "when neither statement and statement_filepath arguments are passed" do
117
172
  it "should fail to register" do
@@ -273,6 +328,48 @@ describe LogStash::Inputs::Jdbc do
273
328
 
274
329
  end
275
330
 
331
+ context "when using target option" do
332
+ let(:settings) do
333
+ {
334
+ "statement" => "SELECT * from test_table FETCH FIRST 1 ROWS ONLY",
335
+ "target" => "sub_field"
336
+ }
337
+ end
338
+
339
+ before do
340
+ plugin.register
341
+ end
342
+
343
+ after do
344
+ plugin.stop
345
+ end
346
+
347
+ it "should put all columns under sub-field" do
348
+ db[:test_table].insert(:num => 1, :custom_time => Time.now.utc, :created_at => Time.now.utc, :string => "Test target option")
349
+
350
+ plugin.run(queue)
351
+
352
+ expect(queue.size).to eq(1)
353
+ event = queue.pop
354
+ expect(event.get("[sub_field][string]")).to eq("Test target option")
355
+ end
356
+ end
357
+
358
+ context "when using target option is not set and ecs_compatibility is enabled" do
359
+ let(:settings) do
360
+ {
361
+ "statement" => "SELECT * from test_table FETCH FIRST 1 ROWS ONLY",
362
+ "ecs_compatibility" => :v1
363
+ }
364
+ end
365
+
366
+ it "should log a warn of missed target usage" do
367
+ expect(plugin.logger).to receive(:info).once.with(/ECS compatibility is enabled but no .*?target.*? was specified/)
368
+
369
+ plugin.register
370
+ end
371
+ end
372
+
276
373
  context "when fetching time data" do
277
374
 
278
375
  let(:settings) do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-integration-jdbc
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.4
4
+ version: 5.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elastic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-06-11 00:00:00.000000000 Z
11
+ date: 2021-05-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -142,6 +142,34 @@ dependencies:
142
142
  - - "<"
143
143
  - !ruby/object:Gem::Version
144
144
  version: '3.5'
145
+ - !ruby/object:Gem::Dependency
146
+ requirement: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - "~>"
149
+ - !ruby/object:Gem::Version
150
+ version: '1.2'
151
+ name: logstash-mixin-ecs_compatibility_support
152
+ prerelease: false
153
+ type: :runtime
154
+ version_requirements: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - "~>"
157
+ - !ruby/object:Gem::Version
158
+ version: '1.2'
159
+ - !ruby/object:Gem::Dependency
160
+ requirement: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - "~>"
163
+ - !ruby/object:Gem::Version
164
+ version: '1.0'
165
+ name: logstash-mixin-validator_support
166
+ prerelease: false
167
+ type: :runtime
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - "~>"
171
+ - !ruby/object:Gem::Version
172
+ version: '1.0'
145
173
  - !ruby/object:Gem::Dependency
146
174
  requirement: !ruby/object:Gem::Requirement
147
175
  requirements:
@@ -234,6 +262,7 @@ files:
234
262
  - lib/logstash/inputs/jdbc.rb
235
263
  - lib/logstash/inputs/tzinfo_jruby_patch.rb
236
264
  - lib/logstash/plugin_mixins/jdbc/checked_count_logger.rb
265
+ - lib/logstash/plugin_mixins/jdbc/common.rb
237
266
  - lib/logstash/plugin_mixins/jdbc/jdbc.rb
238
267
  - lib/logstash/plugin_mixins/jdbc/statement_handler.rb
239
268
  - lib/logstash/plugin_mixins/jdbc/value_tracking.rb