logstash-integration-jdbc 5.0.4 → 5.1.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: 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