fluent-plugin-sql 0.6.1 → 1.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +0 -1
- data/README.md +9 -0
- data/VERSION +1 -1
- data/lib/fluent/plugin/in_sql.rb +27 -39
- data/lib/fluent/plugin/out_sql.rb +41 -41
- data/test/plugin/test_in_sql.rb +12 -8
- data/test/plugin/test_out_sql.rb +12 -11
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3e683b76d37276e4bbe276dc6148216c113e5267
|
4
|
+
data.tar.gz: 374e957bfaf1f9eea818a6d3bc9c9b370c58604f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1a71af14e2817621d22c85ac31a49d54d6b7f224b605ce2febaf09fbe369b435b33df779b23196e37ac6fb5d3c5a26d2039cb5aadf24dc90ec5d105ba4b283c6
|
7
|
+
data.tar.gz: 593be077cfbc429c9502ad7b56c404cac24afbae65268d095c723339bb77a4645a5c4297d03a6cbf73dd269cdda69824d3e2e5d4d4b498406e8871c5e0fb775e
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -7,6 +7,15 @@ This SQL plugin has two parts:
|
|
7
7
|
1. SQL **input** plugin reads records from RDBMSes periodically. An example use case would be getting "diffs" of a table (based on the "updated_at" field).
|
8
8
|
2. SQL **output** plugin that writes records into RDBMes. An example use case would be aggregating server/app/sensor logs into RDBMS systems.
|
9
9
|
|
10
|
+
## Requirements
|
11
|
+
|
12
|
+
| fluent-plugin-sql | fluentd | ruby |
|
13
|
+
|-------------------|------------|--------|
|
14
|
+
| >= 1.0.0 | >= v0.14.4 | >= 2.1 |
|
15
|
+
| < 1.0.0 | < v0.14.0 | >= 1.9 |
|
16
|
+
|
17
|
+
NOTE: fluent-plugin-sql v1.0.0 is now RC. We will release stable v1.0.0 soon.
|
18
|
+
|
10
19
|
## Installation
|
11
20
|
|
12
21
|
$ fluent-gem install fluent-plugin-sql --no-document
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
1.0.0.rc1
|
data/lib/fluent/plugin/in_sql.rb
CHANGED
@@ -16,59 +16,47 @@
|
|
16
16
|
# limitations under the License.
|
17
17
|
#
|
18
18
|
|
19
|
-
require "fluent/input"
|
19
|
+
require "fluent/plugin/input"
|
20
20
|
|
21
|
-
module Fluent
|
21
|
+
module Fluent::Plugin
|
22
22
|
|
23
23
|
require 'active_record'
|
24
24
|
|
25
25
|
class SQLInput < Input
|
26
|
-
Plugin.register_input('sql', self)
|
27
|
-
|
28
|
-
# For fluentd v0.12.16 or earlier
|
29
|
-
class << self
|
30
|
-
unless method_defined?(:desc)
|
31
|
-
def desc(description)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
26
|
+
Fluent::Plugin.register_input('sql', self)
|
35
27
|
|
36
28
|
desc 'RDBMS host'
|
37
29
|
config_param :host, :string
|
38
30
|
desc 'RDBMS port'
|
39
|
-
config_param :port, :integer, :
|
31
|
+
config_param :port, :integer, default: nil
|
40
32
|
desc 'RDBMS driver name.'
|
41
33
|
config_param :adapter, :string
|
42
34
|
desc 'RDBMS database name'
|
43
35
|
config_param :database, :string
|
44
36
|
desc 'RDBMS login user name'
|
45
|
-
config_param :username, :string, :
|
37
|
+
config_param :username, :string, default: nil
|
46
38
|
desc 'RDBMS login password'
|
47
|
-
config_param :password, :string, :
|
39
|
+
config_param :password, :string, default: nil, secret: true
|
48
40
|
desc 'RDBMS socket path'
|
49
|
-
config_param :socket, :string, :
|
41
|
+
config_param :socket, :string, default: nil
|
50
42
|
|
51
43
|
desc 'path to a file to store last rows'
|
52
|
-
config_param :state_file, :string, :
|
44
|
+
config_param :state_file, :string, default: nil
|
53
45
|
desc 'prefix of tags of events. actual tag will be this_tag_prefix.tables_tag (optional)'
|
54
|
-
config_param :tag_prefix, :string, :
|
46
|
+
config_param :tag_prefix, :string, default: nil
|
55
47
|
desc 'interval to run SQLs (optional)'
|
56
|
-
config_param :select_interval, :time, :
|
48
|
+
config_param :select_interval, :time, default: 60
|
57
49
|
desc 'limit of number of rows for each SQL(optional)'
|
58
|
-
config_param :select_limit, :time, :
|
59
|
-
|
60
|
-
unless method_defined?(:log)
|
61
|
-
define_method(:log) { $log }
|
62
|
-
end
|
50
|
+
config_param :select_limit, :time, default: 500
|
63
51
|
|
64
52
|
class TableElement
|
65
|
-
include Configurable
|
53
|
+
include Fluent::Configurable
|
66
54
|
|
67
55
|
config_param :table, :string
|
68
|
-
config_param :tag, :string, :
|
69
|
-
config_param :update_column, :string, :
|
70
|
-
config_param :time_column, :string, :
|
71
|
-
config_param :primary_key, :string, :
|
56
|
+
config_param :tag, :string, default: nil
|
57
|
+
config_param :update_column, :string, default: nil
|
58
|
+
config_param :time_column, :string, default: nil
|
59
|
+
config_param :primary_key, :string, default: nil
|
72
60
|
|
73
61
|
def configure(conf)
|
74
62
|
super
|
@@ -127,9 +115,9 @@ module Fluent
|
|
127
115
|
relation = relation.order("#{@update_column} ASC")
|
128
116
|
relation = relation.limit(limit) if limit > 0
|
129
117
|
|
130
|
-
now = Engine.now
|
118
|
+
now = Fluent::Engine.now
|
131
119
|
|
132
|
-
me = MultiEventStream.new
|
120
|
+
me = Fluent::MultiEventStream.new
|
133
121
|
relation.each do |obj|
|
134
122
|
record = obj.serializable_hash rescue nil
|
135
123
|
if record
|
@@ -181,13 +169,13 @@ module Fluent
|
|
181
169
|
@state_store = @state_file.nil? ? MemoryStateStore.new : StateStore.new(@state_file)
|
182
170
|
|
183
171
|
config = {
|
184
|
-
:
|
185
|
-
:
|
186
|
-
:
|
187
|
-
:
|
188
|
-
:
|
189
|
-
:
|
190
|
-
:
|
172
|
+
adapter: @adapter,
|
173
|
+
host: @host,
|
174
|
+
port: @port,
|
175
|
+
database: @database,
|
176
|
+
username: @username,
|
177
|
+
password: @password,
|
178
|
+
socket: @socket,
|
191
179
|
}
|
192
180
|
|
193
181
|
# creates subclass of ActiveRecord::Base so that it can have different
|
@@ -230,7 +218,7 @@ module Fluent
|
|
230
218
|
log.info "Selecting '#{te.table}' table"
|
231
219
|
false
|
232
220
|
rescue => e
|
233
|
-
log.warn "Can't handle '#{te.table}' table. Ignoring.",
|
221
|
+
log.warn "Can't handle '#{te.table}' table. Ignoring.", error: e
|
234
222
|
log.warn_backtrace e.backtrace
|
235
223
|
true
|
236
224
|
end
|
@@ -264,7 +252,7 @@ module Fluent
|
|
264
252
|
@state_store.last_records[t.table] = t.emit_next_records(last_record, @select_limit)
|
265
253
|
@state_store.update!
|
266
254
|
rescue => e
|
267
|
-
log.error "unexpected error",
|
255
|
+
log.error "unexpected error", error: e
|
268
256
|
log.error_backtrace e.backtrace
|
269
257
|
end
|
270
258
|
end
|
@@ -1,59 +1,52 @@
|
|
1
|
-
require "fluent/output"
|
1
|
+
require "fluent/plugin/output"
|
2
2
|
|
3
|
-
module Fluent
|
4
|
-
class SQLOutput <
|
5
|
-
Plugin.register_output('sql', self)
|
3
|
+
module Fluent::Plugin
|
4
|
+
class SQLOutput < Output
|
5
|
+
Fluent::Plugin.register_output('sql', self)
|
6
6
|
|
7
|
-
|
8
|
-
include SetTagKeyMixin
|
7
|
+
DEFAULT_BUFFER_TYPE = "memory"
|
9
8
|
|
10
|
-
|
11
|
-
class << self
|
12
|
-
unless method_defined?(:desc)
|
13
|
-
def desc(description)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
9
|
+
helpers :inject, :compat_parameters, :event_emitter
|
17
10
|
|
18
11
|
desc 'RDBMS host'
|
19
12
|
config_param :host, :string
|
20
13
|
desc 'RDBMS port'
|
21
|
-
config_param :port, :integer, :
|
14
|
+
config_param :port, :integer, default: nil
|
22
15
|
desc 'RDBMS driver name.'
|
23
16
|
config_param :adapter, :string
|
24
17
|
desc 'RDBMS login user name'
|
25
|
-
config_param :username, :string, :
|
18
|
+
config_param :username, :string, default: nil
|
26
19
|
desc 'RDBMS login password'
|
27
|
-
config_param :password, :string, :
|
20
|
+
config_param :password, :string, default: nil, secret: true
|
28
21
|
desc 'RDBMS database name'
|
29
22
|
config_param :database, :string
|
30
23
|
desc 'RDBMS socket path'
|
31
|
-
config_param :socket, :string, :
|
24
|
+
config_param :socket, :string, default: nil
|
32
25
|
desc 'remove the given prefix from the events'
|
33
|
-
config_param :remove_tag_prefix, :string, :
|
26
|
+
config_param :remove_tag_prefix, :string, default: nil
|
34
27
|
desc 'enable fallback'
|
35
|
-
config_param :enable_fallback, :bool, :
|
36
|
-
|
37
|
-
attr_accessor :tables
|
28
|
+
config_param :enable_fallback, :bool, default: true
|
38
29
|
|
39
|
-
|
40
|
-
|
30
|
+
config_section :buffer do
|
31
|
+
config_set_default :@type, DEFAULT_BUFFER_TYPE
|
41
32
|
end
|
42
33
|
|
34
|
+
attr_accessor :tables
|
35
|
+
|
43
36
|
# TODO: Merge SQLInput's TableElement
|
44
37
|
class TableElement
|
45
|
-
include Configurable
|
38
|
+
include Fluent::Configurable
|
46
39
|
|
47
40
|
config_param :table, :string
|
48
41
|
config_param :column_mapping, :string
|
49
|
-
config_param :num_retries, :integer, :
|
42
|
+
config_param :num_retries, :integer, default: 5
|
50
43
|
|
51
44
|
attr_reader :model
|
52
45
|
attr_reader :pattern
|
53
46
|
|
54
47
|
def initialize(pattern, log, enable_fallback)
|
55
48
|
super()
|
56
|
-
@pattern = MatchPattern.create(pattern)
|
49
|
+
@pattern = Fluent::MatchPattern.create(pattern)
|
57
50
|
@log = log
|
58
51
|
@enable_fallback = enable_fallback
|
59
52
|
end
|
@@ -95,7 +88,7 @@ module Fluent
|
|
95
88
|
# format process should be moved to emit / format after supports error stream.
|
96
89
|
records << @model.new(@format_proc.call(data))
|
97
90
|
rescue => e
|
98
|
-
args = {
|
91
|
+
args = {error: e, table: @table, record: Yajl.dump(data)}
|
99
92
|
@log.warn "Failed to create the model. Ignore a record:", args
|
100
93
|
end
|
101
94
|
}
|
@@ -104,10 +97,10 @@ module Fluent
|
|
104
97
|
rescue ActiveRecord::StatementInvalid, ActiveRecord::Import::MissingColumnError => e
|
105
98
|
if @enable_fallback
|
106
99
|
# ignore other exceptions to use Fluentd retry mechanizm
|
107
|
-
@log.warn "Got deterministic error. Fallback to one-by-one import",
|
100
|
+
@log.warn "Got deterministic error. Fallback to one-by-one import", error: e
|
108
101
|
one_by_one_import(records)
|
109
102
|
else
|
110
|
-
$log.warn "Got deterministic error. Fallback is disabled",
|
103
|
+
$log.warn "Got deterministic error. Fallback is disabled", error: e
|
111
104
|
raise e
|
112
105
|
end
|
113
106
|
end
|
@@ -119,15 +112,15 @@ module Fluent
|
|
119
112
|
begin
|
120
113
|
@model.import([record])
|
121
114
|
rescue ActiveRecord::StatementInvalid, ActiveRecord::Import::MissingColumnError => e
|
122
|
-
@log.error "Got deterministic error again. Dump a record",
|
115
|
+
@log.error "Got deterministic error again. Dump a record", error: e, record: record
|
123
116
|
rescue => e
|
124
117
|
retries += 1
|
125
118
|
if retries > @num_retries
|
126
|
-
@log.error "Can't recover undeterministic error. Dump a record",
|
119
|
+
@log.error "Can't recover undeterministic error. Dump a record", error: e, record: record
|
127
120
|
next
|
128
121
|
end
|
129
122
|
|
130
|
-
@log.warn "Failed to import a record: retry number = #{retries}",
|
123
|
+
@log.warn "Failed to import a record: retry number = #{retries}", error: e
|
131
124
|
sleep 0.5
|
132
125
|
retry
|
133
126
|
end
|
@@ -154,6 +147,8 @@ module Fluent
|
|
154
147
|
end
|
155
148
|
|
156
149
|
def configure(conf)
|
150
|
+
compat_parameters_convert(conf, :inject, :buffer)
|
151
|
+
|
157
152
|
super
|
158
153
|
|
159
154
|
if remove_tag_prefix = conf['remove_tag_prefix']
|
@@ -177,7 +172,7 @@ module Fluent
|
|
177
172
|
@only_default = @tables.empty?
|
178
173
|
|
179
174
|
if @default_table.nil?
|
180
|
-
raise ConfigError, "There is no default table. <table> is required in sql output"
|
175
|
+
raise Fluent::ConfigError, "There is no default table. <table> is required in sql output"
|
181
176
|
end
|
182
177
|
end
|
183
178
|
|
@@ -185,13 +180,13 @@ module Fluent
|
|
185
180
|
super
|
186
181
|
|
187
182
|
config = {
|
188
|
-
:
|
189
|
-
:
|
190
|
-
:
|
191
|
-
:
|
192
|
-
:
|
193
|
-
:
|
194
|
-
:
|
183
|
+
adapter: @adapter,
|
184
|
+
host: @host,
|
185
|
+
port: @port,
|
186
|
+
database: @database,
|
187
|
+
username: @username,
|
188
|
+
password: @password,
|
189
|
+
socket: @socket,
|
195
190
|
}
|
196
191
|
|
197
192
|
@base_model = Class.new(ActiveRecord::Base) do
|
@@ -221,9 +216,14 @@ module Fluent
|
|
221
216
|
end
|
222
217
|
|
223
218
|
def format(tag, time, record)
|
219
|
+
record = inject_values_to_record(tag, time, record)
|
224
220
|
[tag, time, record].to_msgpack
|
225
221
|
end
|
226
222
|
|
223
|
+
def formatted_to_msgpack_binary
|
224
|
+
true
|
225
|
+
end
|
226
|
+
|
227
227
|
def write(chunk)
|
228
228
|
ActiveRecord::Base.connection_pool.with_connection do
|
229
229
|
|
@@ -244,7 +244,7 @@ module Fluent
|
|
244
244
|
log.info "Selecting '#{te.table}' table"
|
245
245
|
false
|
246
246
|
rescue => e
|
247
|
-
log.warn "Can't handle '#{te.table}' table. Ignoring.",
|
247
|
+
log.warn "Can't handle '#{te.table}' table. Ignoring.", error: e
|
248
248
|
log.warn_backtrace e.backtrace
|
249
249
|
true
|
250
250
|
end
|
data/test/plugin/test_in_sql.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "helper"
|
2
|
+
require "fluent/test/driver/input"
|
2
3
|
|
3
4
|
class SqlInputTest < Test::Unit::TestCase
|
4
5
|
def setup
|
@@ -28,7 +29,7 @@ class SqlInputTest < Test::Unit::TestCase
|
|
28
29
|
]
|
29
30
|
|
30
31
|
def create_driver(conf = CONFIG)
|
31
|
-
Fluent::Test::
|
32
|
+
Fluent::Test::Driver::Input.new(Fluent::Plugin::SQLInput).configure(conf)
|
32
33
|
end
|
33
34
|
|
34
35
|
def test_configure
|
@@ -65,18 +66,21 @@ class SqlInputTest < Test::Unit::TestCase
|
|
65
66
|
Message.create!(message: "message 2")
|
66
67
|
Message.create!(message: "message 3")
|
67
68
|
|
69
|
+
d.end_if do
|
70
|
+
d.record_count >= 3
|
71
|
+
end
|
68
72
|
d.run
|
69
73
|
|
70
|
-
assert_equal("db.logs", d.
|
74
|
+
assert_equal("db.logs", d.events[0][0])
|
71
75
|
expected = [
|
72
|
-
[d.
|
73
|
-
[d.
|
74
|
-
[d.
|
76
|
+
[d.events[0][1], "message 1"],
|
77
|
+
[d.events[1][1], "message 2"],
|
78
|
+
[d.events[2][1], "message 3"],
|
75
79
|
]
|
76
80
|
actual = [
|
77
|
-
[Time.parse(d.
|
78
|
-
[Time.parse(d.
|
79
|
-
[Time.parse(d.
|
81
|
+
[Time.parse(d.events[0][2]["updated_at"]).to_i, d.events[0][2]["message"]],
|
82
|
+
[Time.parse(d.events[1][2]["updated_at"]).to_i, d.events[1][2]["message"]],
|
83
|
+
[Time.parse(d.events[2][2]["updated_at"]).to_i, d.events[2][2]["message"]],
|
80
84
|
]
|
81
85
|
assert_equal(expected, actual)
|
82
86
|
end
|
data/test/plugin/test_out_sql.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "helper"
|
2
|
+
require "fluent/test/driver/output"
|
2
3
|
|
3
4
|
class SqlOutputTest < Test::Unit::TestCase
|
4
5
|
def setup
|
@@ -26,7 +27,7 @@ class SqlOutputTest < Test::Unit::TestCase
|
|
26
27
|
]
|
27
28
|
|
28
29
|
def create_driver(conf = CONFIG)
|
29
|
-
Fluent::Test::
|
30
|
+
Fluent::Test::Driver::Output.new(Fluent::Plugin::SQLOutput).configure(conf)
|
30
31
|
end
|
31
32
|
|
32
33
|
def test_configure
|
@@ -61,10 +62,10 @@ class SqlOutputTest < Test::Unit::TestCase
|
|
61
62
|
d = create_driver
|
62
63
|
time = Time.parse("2011-01-02 13:14:15 UTC").to_i
|
63
64
|
|
64
|
-
d.
|
65
|
-
|
66
|
-
|
67
|
-
|
65
|
+
d.run(default_tag: 'test') do
|
66
|
+
d.feed(time, {"message" => "message1"})
|
67
|
+
d.feed(time, {"message" => "message2"})
|
68
|
+
end
|
68
69
|
|
69
70
|
default_table = d.instance.instance_variable_get(:@default_table)
|
70
71
|
model = default_table.instance_variable_get(:@model)
|
@@ -78,10 +79,10 @@ class SqlOutputTest < Test::Unit::TestCase
|
|
78
79
|
d = create_driver
|
79
80
|
time = Time.parse("2011-01-02 13:14:15 UTC").to_i
|
80
81
|
|
81
|
-
d.
|
82
|
-
|
82
|
+
d.run(default_tag: 'test') do
|
83
|
+
d.feed(time, {"message" => "message1"})
|
84
|
+
d.feed(time, {"message" => "message2"})
|
83
85
|
|
84
|
-
d.run do
|
85
86
|
default_table = d.instance.instance_variable_get(:@default_table)
|
86
87
|
model = default_table.instance_variable_get(:@model)
|
87
88
|
mock(model).import(anything).at_least(1) do
|
@@ -95,10 +96,10 @@ class SqlOutputTest < Test::Unit::TestCase
|
|
95
96
|
d = create_driver
|
96
97
|
time = Time.parse("2011-01-02 13:14:15 UTC").to_i
|
97
98
|
|
98
|
-
d.
|
99
|
-
|
99
|
+
d.run(default_tag: 'test') do
|
100
|
+
d.feed(time, {"message" => "message1"})
|
101
|
+
d.feed(time, {"message" => "message2"})
|
100
102
|
|
101
|
-
d.run do
|
102
103
|
default_table = d.instance.instance_variable_get(:@default_table)
|
103
104
|
model = default_table.instance_variable_get(:@model)
|
104
105
|
mock(model).import([anything, anything]).once do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fluent-plugin-sql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0.rc1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sadayuki Furuhashi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-10-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: fluentd
|
@@ -162,12 +162,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
162
162
|
version: '0'
|
163
163
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
164
164
|
requirements:
|
165
|
-
- - "
|
165
|
+
- - ">"
|
166
166
|
- !ruby/object:Gem::Version
|
167
|
-
version:
|
167
|
+
version: 1.3.1
|
168
168
|
requirements: []
|
169
169
|
rubyforge_project:
|
170
|
-
rubygems_version: 2.6.
|
170
|
+
rubygems_version: 2.6.13
|
171
171
|
signing_key:
|
172
172
|
specification_version: 4
|
173
173
|
summary: SQL input/output plugin for Fluentd event collector
|