logstash-output-jdbc 0.2.10 → 5.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +64 -0
  3. data/Gemfile +11 -0
  4. data/README.md +54 -30
  5. data/THANKS.md +18 -0
  6. data/lib/com/zaxxer/HikariCP/2.4.7/HikariCP-2.4.7.jar +0 -0
  7. data/lib/com/zaxxer/HikariCP/2.7.2/HikariCP-2.7.2.jar +0 -0
  8. data/lib/logstash-output-jdbc_jars.rb +2 -2
  9. data/lib/logstash/outputs/jdbc.rb +163 -163
  10. data/lib/org/apache/logging/log4j/log4j-api/2.6.2/log4j-api-2.6.2.jar +0 -0
  11. data/lib/org/apache/logging/log4j/log4j-api/2.9.1/log4j-api-2.9.1.jar +0 -0
  12. data/lib/org/apache/logging/log4j/log4j-core/2.9.1/log4j-core-2.9.1.jar +0 -0
  13. data/lib/org/apache/logging/log4j/log4j-slf4j-impl/2.6.2/log4j-slf4j-impl-2.6.2.jar +0 -0
  14. data/lib/org/apache/logging/log4j/log4j-slf4j-impl/2.9.1/log4j-slf4j-impl-2.9.1.jar +0 -0
  15. data/lib/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.jar +0 -0
  16. data/lib/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar +0 -0
  17. data/logstash-output-jdbc.gemspec +32 -0
  18. data/spec/jdbc_spec_helper.rb +216 -0
  19. data/spec/outputs/jdbc_derby_spec.rb +33 -0
  20. data/spec/outputs/jdbc_mysql_spec.rb +24 -0
  21. data/spec/outputs/jdbc_postgres_spec.rb +41 -0
  22. data/spec/outputs/jdbc_spec.rb +4 -87
  23. data/spec/outputs/jdbc_sqlite_spec.rb +26 -0
  24. data/vendor/jar-dependencies/runtime-jars/HikariCP-2.7.2.jar +0 -0
  25. data/vendor/jar-dependencies/runtime-jars/log4j-api-2.6.2.jar +0 -0
  26. data/vendor/jar-dependencies/runtime-jars/log4j-slf4j-impl-2.6.2.jar +0 -0
  27. data/vendor/jar-dependencies/runtime-jars/slf4j-api-1.7.25.jar +0 -0
  28. metadata +94 -53
  29. data/LICENSE.txt +0 -21
  30. data/lib/com/zaxxer/HikariCP/2.4.2/HikariCP-2.4.2.jar +0 -0
  31. data/lib/com/zaxxer/HikariCP/2.4.6/HikariCP-2.4.6.jar +0 -0
  32. data/lib/logstash-output-jdbc_ring-buffer.rb +0 -17
  33. data/lib/org/slf4j/slf4j-api/1.7.12/slf4j-api-1.7.12.jar +0 -0
  34. data/lib/org/slf4j/slf4j-api/1.7.16/slf4j-api-1.7.16.jar +0 -0
  35. data/lib/org/slf4j/slf4j-log4j12/1.7.13/slf4j-log4j12-1.7.13.jar +0 -0
  36. data/vendor/jar-dependencies/runtime-jars/HikariCP-2.4.2.jar +0 -0
  37. data/vendor/jar-dependencies/runtime-jars/HikariCP-2.4.6.jar +0 -0
  38. data/vendor/jar-dependencies/runtime-jars/log4j-1.2.17.jar +0 -0
  39. data/vendor/jar-dependencies/runtime-jars/slf4j-api-1.7.13.jar +0 -0
  40. data/vendor/jar-dependencies/runtime-jars/slf4j-api-1.7.16.jar +0 -0
  41. data/vendor/jar-dependencies/runtime-jars/slf4j-log4j12-1.7.21.jar +0 -0
  42. data/vendor/jar-dependencies/runtime-jars/slf4j-nop-1.7.13.jar +0 -0
@@ -0,0 +1,32 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'logstash-output-jdbc'
3
+ s.version = '5.4.0'
4
+ s.licenses = ['Apache License (2.0)']
5
+ s.summary = 'This plugin allows you to output to SQL, via JDBC'
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 'logstash-output-jdbc'. This gem is not a stand-alone program"
7
+ s.authors = ['the_angry_angel']
8
+ s.email = 'karl+github@theangryangel.co.uk'
9
+ s.homepage = 'https://github.com/theangryangel/logstash-output-jdbc'
10
+ s.require_paths = ['lib']
11
+
12
+ # Files
13
+ s.files = Dir['lib/**/*','spec/**/*','vendor/**/*','*.gemspec','*.md','CONTRIBUTORS','Gemfile','LICENSE','NOTICE.TXT']
14
+ # Tests
15
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
16
+
17
+ # Special flag to let us know this is actually a logstash plugin
18
+ s.metadata = { 'logstash_plugin' => 'true', 'logstash_group' => 'output' }
19
+
20
+ # Gem dependencies
21
+ #
22
+ s.add_runtime_dependency 'logstash-core-plugin-api', ">= 1.60", "<= 2.99"
23
+ s.add_runtime_dependency 'logstash-codec-plain'
24
+ s.add_development_dependency 'logstash-devutils'
25
+
26
+ s.requirements << "jar 'com.zaxxer:HikariCP', '2.7.2'"
27
+ s.requirements << "jar 'org.apache.logging.log4j:log4j-slf4j-impl', '2.6.2'"
28
+
29
+ s.add_development_dependency 'jar-dependencies'
30
+ s.add_development_dependency 'ruby-maven', '~> 3.3'
31
+ s.add_development_dependency 'rubocop', '0.41.2'
32
+ end
@@ -0,0 +1,216 @@
1
+ require 'logstash/devutils/rspec/spec_helper'
2
+ require 'logstash/outputs/jdbc'
3
+ require 'stud/temporary'
4
+ require 'java'
5
+ require 'securerandom'
6
+
7
+ RSpec::Support::ObjectFormatter.default_instance.max_formatted_output_length = 80000
8
+
9
+ RSpec.configure do |c|
10
+
11
+ def start_service(name)
12
+ cmd = "sudo /etc/init.d/#{name}* start"
13
+
14
+ `which systemctl`
15
+ if $?.success?
16
+ cmd = "sudo systemctl start #{name}"
17
+ end
18
+
19
+ `#{cmd}`
20
+ end
21
+
22
+ def stop_service(name)
23
+ cmd = "sudo /etc/init.d/#{name}* stop"
24
+
25
+ `which systemctl`
26
+ if $?.success?
27
+ cmd = "sudo systemctl stop #{name}"
28
+ end
29
+
30
+ `#{cmd}`
31
+ end
32
+
33
+ end
34
+
35
+ RSpec.shared_context 'rspec setup' do
36
+ it 'ensure jar is available' do
37
+ expect(ENV[jdbc_jar_env]).not_to be_nil, "#{jdbc_jar_env} not defined, required to run tests"
38
+ expect(File.exist?(ENV[jdbc_jar_env])).to eq(true), "#{jdbc_jar_env} defined, but not valid"
39
+ end
40
+ end
41
+
42
+ RSpec.shared_context 'when initializing' do
43
+ it 'shouldn\'t register with a missing jar file' do
44
+ jdbc_settings['driver_jar_path'] = nil
45
+ plugin = LogStash::Plugin.lookup('output', 'jdbc').new(jdbc_settings)
46
+ expect { plugin.register }.to raise_error(LogStash::ConfigurationError)
47
+ end
48
+ end
49
+
50
+ RSpec.shared_context 'when outputting messages' do
51
+ let(:logger) {
52
+ double("logger")
53
+ }
54
+
55
+ let(:jdbc_test_table) do
56
+ 'logstash_output_jdbc_test'
57
+ end
58
+
59
+ let(:jdbc_drop_table) do
60
+ "DROP TABLE #{jdbc_test_table}"
61
+ end
62
+
63
+ let(:jdbc_statement_fields) do
64
+ [
65
+ {db_field: "created_at", db_type: "datetime", db_value: '?', event_field: '@timestamp'},
66
+ {db_field: "message", db_type: "varchar(512)", db_value: '?', event_field: 'message'},
67
+ {db_field: "message_sprintf", db_type: "varchar(512)", db_value: '?', event_field: 'sprintf-%{message}'},
68
+ {db_field: "static_int", db_type: "int", db_value: '?', event_field: 'int'},
69
+ {db_field: "static_bigint", db_type: "bigint", db_value: '?', event_field: 'bigint'},
70
+ {db_field: "static_float", db_type: "float", db_value: '?', event_field: 'float'},
71
+ {db_field: "static_bool", db_type: "boolean", db_value: '?', event_field: 'bool'},
72
+ {db_field: "static_bigdec", db_type: "decimal", db_value: '?', event_field: 'bigdec'}
73
+ ]
74
+ end
75
+
76
+ let(:jdbc_create_table) do
77
+ fields = jdbc_statement_fields.collect { |entry| "#{entry[:db_field]} #{entry[:db_type]} not null" }.join(", ")
78
+
79
+ "CREATE table #{jdbc_test_table} (#{fields})"
80
+ end
81
+
82
+ let(:jdbc_drop_table) do
83
+ "DROP table #{jdbc_test_table}"
84
+ end
85
+
86
+ let(:jdbc_statement) do
87
+ fields = jdbc_statement_fields.collect { |entry| "#{entry[:db_field]}" }.join(", ")
88
+ values = jdbc_statement_fields.collect { |entry| "#{entry[:db_value]}" }.join(", ")
89
+ statement = jdbc_statement_fields.collect { |entry| entry[:event_field] }
90
+
91
+ statement.insert(0, "insert into #{jdbc_test_table} (#{fields}) values(#{values})")
92
+ end
93
+
94
+ let(:systemd_database_service) do
95
+ nil
96
+ end
97
+
98
+ let(:event) do
99
+ # TODO: Auto generate fields from jdbc_statement_fields
100
+ LogStash::Event.new({
101
+ message: "test-message #{SecureRandom.uuid}",
102
+ float: 12.1,
103
+ bigint: 4000881632477184,
104
+ bool: true,
105
+ int: 1,
106
+ bigdec: BigDecimal.new("123.123")
107
+ })
108
+ end
109
+
110
+ let(:plugin) do
111
+ # Setup logger
112
+ allow(LogStash::Outputs::Jdbc).to receive(:logger).and_return(logger)
113
+
114
+ # XXX: Suppress reflection logging. There has to be a better way around this.
115
+ allow(logger).to receive(:debug).with(/config LogStash::/)
116
+
117
+ # Suppress beta warnings.
118
+ allow(logger).to receive(:info).with(/Please let us know if you find bugs or have suggestions on how to improve this plugin./)
119
+
120
+ # Suppress start up messages.
121
+ expect(logger).to receive(:info).once.with(/JDBC - Starting up/)
122
+
123
+ # Setup plugin
124
+ output = LogStash::Plugin.lookup('output', 'jdbc').new(jdbc_settings)
125
+ output.register
126
+
127
+ output
128
+ end
129
+
130
+ before :each do
131
+ # Setup table
132
+ c = plugin.instance_variable_get(:@pool).getConnection
133
+
134
+ # Derby doesn't support IF EXISTS.
135
+ # Seems like the quickest solution. Bleurgh.
136
+ begin
137
+ stmt = c.createStatement
138
+ stmt.executeUpdate(jdbc_drop_table)
139
+ rescue
140
+ # noop
141
+ ensure
142
+ stmt.close
143
+
144
+ stmt = c.createStatement
145
+ stmt.executeUpdate(jdbc_create_table)
146
+ stmt.close
147
+ c.close
148
+ end
149
+ end
150
+
151
+ # Delete table after each
152
+ after :each do
153
+ c = plugin.instance_variable_get(:@pool).getConnection
154
+
155
+ stmt = c.createStatement
156
+ stmt.executeUpdate(jdbc_drop_table)
157
+ stmt.close
158
+ c.close
159
+ end
160
+
161
+ it 'should save a event' do
162
+ expect { plugin.multi_receive([event]) }.to_not raise_error
163
+
164
+ # Verify the number of items in the output table
165
+ c = plugin.instance_variable_get(:@pool).getConnection
166
+
167
+ # TODO replace this simple count with a check of the actual contents
168
+
169
+ stmt = c.prepareStatement("select count(*) as total from #{jdbc_test_table} where message = ?")
170
+ stmt.setString(1, event.get('message'))
171
+ rs = stmt.executeQuery
172
+ count = 0
173
+ count = rs.getInt('total') while rs.next
174
+ stmt.close
175
+ c.close
176
+
177
+ expect(count).to eq(1)
178
+ end
179
+
180
+ it 'should not save event, and log an unretryable exception' do
181
+ e = event
182
+ original_event = e.get('message')
183
+ e.set('message', nil)
184
+
185
+ expect(logger).to receive(:error).once.with(/JDBC - Exception. Not retrying/, Hash)
186
+ expect { plugin.multi_receive([event]) }.to_not raise_error
187
+
188
+ e.set('message', original_event)
189
+ end
190
+
191
+ it 'it should retry after a connection loss, and log a warning' do
192
+ skip "does not run as a service, or known issue with test" if systemd_database_service.nil?
193
+
194
+ p = plugin
195
+
196
+ # Check that everything is fine right now
197
+ expect { p.multi_receive([event]) }.not_to raise_error
198
+
199
+ stop_service(systemd_database_service)
200
+
201
+ # Start a thread to restart the service after the fact.
202
+ t = Thread.new(systemd_database_service) { |systemd_database_service|
203
+ sleep 20
204
+
205
+ start_service(systemd_database_service)
206
+ }
207
+
208
+ t.run
209
+
210
+ expect(logger).to receive(:warn).at_least(:once).with(/JDBC - Exception. Retrying/, Hash)
211
+ expect { p.multi_receive([event]) }.to_not raise_error
212
+
213
+ # Wait for the thread to finish
214
+ t.join
215
+ end
216
+ end
@@ -0,0 +1,33 @@
1
+ require_relative '../jdbc_spec_helper'
2
+
3
+ describe 'logstash-output-jdbc: derby', if: ENV['JDBC_DERBY_JAR'] do
4
+ include_context 'rspec setup'
5
+ include_context 'when outputting messages'
6
+
7
+ let(:jdbc_jar_env) do
8
+ 'JDBC_DERBY_JAR'
9
+ end
10
+
11
+ let(:jdbc_statement_fields) do
12
+ [
13
+ {db_field: "created_at", db_type: "timestamp", db_value: 'CAST(? as timestamp)', event_field: '@timestamp'},
14
+ {db_field: "message", db_type: "varchar(512)", db_value: '?', event_field: 'message'},
15
+ {db_field: "message_sprintf", db_type: "varchar(512)", db_value: '?', event_field: 'sprintf-%{message}'},
16
+ {db_field: "static_int", db_type: "int", db_value: '?', event_field: 'int'},
17
+ {db_field: "static_bigint", db_type: "bigint", db_value: '?', event_field: 'bigint'},
18
+ {db_field: "static_float", db_type: "float", db_value: '?', event_field: 'float'},
19
+ {db_field: "static_bool", db_type: "boolean", db_value: '?', event_field: 'bool'},
20
+ {db_field: "static_bigdec", db_type: "decimal", db_value: '?', event_field: 'bigdec'}
21
+ ]
22
+ end
23
+
24
+ let(:jdbc_settings) do
25
+ {
26
+ 'driver_class' => 'org.apache.derby.jdbc.EmbeddedDriver',
27
+ 'connection_string' => 'jdbc:derby:memory:testdb;create=true',
28
+ 'driver_jar_path' => ENV[jdbc_jar_env],
29
+ 'statement' => jdbc_statement,
30
+ 'max_flush_exceptions' => 1
31
+ }
32
+ end
33
+ end
@@ -0,0 +1,24 @@
1
+ require_relative '../jdbc_spec_helper'
2
+
3
+ describe 'logstash-output-jdbc: mysql', if: ENV['JDBC_MYSQL_JAR'] do
4
+ include_context 'rspec setup'
5
+ include_context 'when outputting messages'
6
+
7
+ let(:jdbc_jar_env) do
8
+ 'JDBC_MYSQL_JAR'
9
+ end
10
+
11
+ let(:systemd_database_service) do
12
+ 'mysql'
13
+ end
14
+
15
+ let(:jdbc_settings) do
16
+ {
17
+ 'driver_class' => 'com.mysql.jdbc.Driver',
18
+ 'connection_string' => 'jdbc:mysql://localhost/logstash?user=logstash&password=logstash',
19
+ 'driver_jar_path' => ENV[jdbc_jar_env],
20
+ 'statement' => jdbc_statement,
21
+ 'max_flush_exceptions' => 1
22
+ }
23
+ end
24
+ end
@@ -0,0 +1,41 @@
1
+ require_relative '../jdbc_spec_helper'
2
+
3
+ describe 'logstash-output-jdbc: postgres', if: ENV['JDBC_POSTGRES_JAR'] do
4
+ include_context 'rspec setup'
5
+ include_context 'when outputting messages'
6
+
7
+ let(:jdbc_jar_env) do
8
+ 'JDBC_POSTGRES_JAR'
9
+ end
10
+
11
+ # TODO: Postgres doesnt kill connections fast enough for the test to pass
12
+ # Investigate options.
13
+
14
+ #let(:systemd_database_service) do
15
+ # 'postgresql'
16
+ #end
17
+
18
+ let(:jdbc_statement_fields) do
19
+ [
20
+ {db_field: "created_at", db_type: "timestamp", db_value: 'CAST(? as timestamp)', event_field: '@timestamp'},
21
+ {db_field: "message", db_type: "varchar(512)", db_value: '?', event_field: 'message'},
22
+ {db_field: "message_sprintf", db_type: "varchar(512)", db_value: '?', event_field: 'sprintf-%{message}'},
23
+ {db_field: "static_int", db_type: "int", db_value: '?', event_field: 'int'},
24
+ {db_field: "static_bigint", db_type: "bigint", db_value: '?', event_field: 'bigint'},
25
+ {db_field: "static_float", db_type: "float", db_value: '?', event_field: 'float'},
26
+ {db_field: "static_bool", db_type: "boolean", db_value: '?', event_field: 'bool'},
27
+ {db_field: "static_bigdec", db_type: "decimal", db_value: '?', event_field: 'bigdec'}
28
+
29
+ ]
30
+ end
31
+
32
+ let(:jdbc_settings) do
33
+ {
34
+ 'driver_class' => 'org.postgresql.Driver',
35
+ 'connection_string' => 'jdbc:postgresql://localhost/logstash?user=logstash&password=logstash',
36
+ 'driver_jar_path' => ENV[jdbc_jar_env],
37
+ 'statement' => jdbc_statement,
38
+ 'max_flush_exceptions' => 1
39
+ }
40
+ end
41
+ end
@@ -1,94 +1,11 @@
1
- require "logstash/devutils/rspec/spec_helper"
2
- require "logstash/outputs/jdbc"
3
- require "stud/temporary"
4
- require "java"
1
+ require_relative '../jdbc_spec_helper'
5
2
 
6
3
  describe LogStash::Outputs::Jdbc do
7
-
8
- let(:derby_settings) do
9
- {
10
- "driver_class" => "org.apache.derby.jdbc.EmbeddedDriver",
11
- "connection_string" => "jdbc:derby:memory:testdb;create=true",
12
- "driver_jar_path" => ENV['JDBC_DERBY_JAR'],
13
- "statement" => [ "insert into log (created_at, message) values(?, ?)", "@timestamp" "message" ]
14
- }
15
- end
16
-
17
- context 'rspec setup' do
18
-
19
- it 'ensure derby is available' do
20
- j = ENV['JDBC_DERBY_JAR']
21
- expect(j).not_to be_nil, "JDBC_DERBY_JAR not defined, required to run tests"
22
- expect(File.exists?(j)).to eq(true), "JDBC_DERBY_JAR defined, but not valid"
23
- end
24
-
25
- end
26
-
27
4
  context 'when initializing' do
28
-
29
5
  it 'shouldn\'t register without a config' do
30
- expect {
31
- LogStash::Plugin.lookup("output", "jdbc").new()
32
- }.to raise_error(LogStash::ConfigurationError)
33
- end
34
-
35
- it 'shouldn\'t register with a missing jar file' do
36
- derby_settings['driver_jar_path'] = nil
37
- plugin = LogStash::Plugin.lookup("output", "jdbc").new(derby_settings)
38
- expect { plugin.register }.to raise_error
6
+ expect do
7
+ LogStash::Plugin.lookup('output', 'jdbc').new
8
+ end.to raise_error(LogStash::ConfigurationError)
39
9
  end
40
-
41
- it 'shouldn\'t register with a missing jar file' do
42
- derby_settings['connection_string'] = nil
43
- plugin = LogStash::Plugin.lookup("output", "jdbc").new(derby_settings)
44
- expect { plugin.register }.to raise_error
45
- end
46
-
47
10
  end
48
-
49
- context 'when outputting messages' do
50
-
51
- let(:event_fields) do
52
- { message: 'test-message' }
53
- end
54
- let(:event) { LogStash::Event.new(event_fields) }
55
- let(:plugin) {
56
- # Setup plugin
57
- output = LogStash::Plugin.lookup("output", "jdbc").new(derby_settings)
58
- output.register
59
- if ENV['JDBC_DEBUG'] == '1'
60
- output.logger.subscribe(STDOUT)
61
- end
62
-
63
- # Setup table
64
- c = output.instance_variable_get(:@pool).getConnection()
65
- stmt = c.createStatement()
66
- stmt.executeUpdate("CREATE table log (created_at timestamp, message varchar(512))")
67
- stmt.close()
68
- c.close()
69
-
70
- output
71
- }
72
-
73
- it 'should save a event' do
74
- expect { plugin.receive(event) }.to_not raise_error
75
-
76
- # Wait for 1 second, for the buffer to flush
77
- sleep 1
78
-
79
- c = plugin.instance_variable_get(:@pool).getConnection()
80
- stmt = c.createStatement()
81
- rs = stmt.executeQuery("select count(*) as total from log")
82
- count = 0
83
- while rs.next()
84
- count = rs.getInt("total")
85
- end
86
- stmt.close()
87
- c.close()
88
-
89
- expect(count).to be > 0
90
- end
91
-
92
- end
93
-
94
11
  end