fluent-plugin-sql 0.6.1 → 2.1.0

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
- SHA1:
3
- metadata.gz: 8bdf7e835bed062213f63778607f5f4e3a6f9a02
4
- data.tar.gz: d9b7d721ef9ad035193fbf0eb5a5460e88caa9aa
2
+ SHA256:
3
+ metadata.gz: '00987a628d7afc94546e68d5374331e82c65eca49aa8e3aad33b8f5ffb5c8f59'
4
+ data.tar.gz: e809d69583ab25571878d9bc4adb64a26841e14347e9deb973e1a659262bbf47
5
5
  SHA512:
6
- metadata.gz: ab807cd58fa29576d9164e4ab1e307a3b8cde72dd129e608acc78da01c15499e9ba9def596b044604bd2890c877fc3d66be79ca31d441e24f0d6f5cba8a741f6
7
- data.tar.gz: 23a7698d628cd9dad85b9d1ab8f50ade3d925531cb230cbc9a93ace20e03ab157bd0ed09c1a3f97a9380931135acda9837402249e6b360e1881f694b157eff3e
6
+ metadata.gz: b5cf74d0668a3e7b26220d2b56179c3668a99e2a51e8d99b7468d797bdc3c7e0fe81ca813ede95713fc3a2dc6484929505953c0be7239129343f138798ad7fe4
7
+ data.tar.gz: c45922cd85d83e3ed89629c55232cd0fe83106b4961f1d2dfaafd8564d8db900b4740f78e2f12b7596ee6ce025a43efaf4cb409ca9ed418434dcc3ed447a4456
@@ -5,19 +5,17 @@ addons:
5
5
  postgresql: "9.4"
6
6
 
7
7
  rvm:
8
- - 2.0
9
- - 2.1
10
- - 2.2.4
11
- - 2.3.1
12
- - 2.4.1
8
+ - 2.4.10
9
+ - 2.5.8
10
+ - 2.6.6
11
+ - 2.7.1
13
12
  - ruby-head
14
13
 
15
14
  gemfile:
16
15
  - Gemfile
17
- - Gemfile.v0.12
18
16
 
19
17
  before_install:
20
- - gem install bundler
18
+ - gem update bundler
21
19
 
22
20
  before_script:
23
21
  - psql -U postgres -c "CREATE ROLE fluentd WITH LOGIN ENCRYPTED PASSWORD 'fluentd';"
@@ -28,6 +26,3 @@ script: bundle exec rake test
28
26
  matrix:
29
27
  allow_failures:
30
28
  - rvm: ruby-head
31
- exclude:
32
- - rvm: 2.0
33
- gemfile: Gemfile
@@ -0,0 +1,20 @@
1
+ Release 2.1.0 - 2020/09/09
2
+
3
+  * in_sql: Use Fluent::EventTime instead of Integer to preserve millisecond precision
4
+
5
+ Release 2.0.0 - 2020/04/22
6
+
7
+ * out_sql: Remove v0.12 API code and use full v1 API. Change buffer format.
8
+
9
+ Release 1.1.1 - 2019/05/10
10
+
11
+ * out_sql: Support schema_search_path option of PostgreSQL
12
+
13
+ Release 1.1.0 - 2018/10/04
14
+
15
+ * Upgrade ActiveRecord to 5.1
16
+
17
+ Release 1.0.0 - 2018/04/06
18
+
19
+ * Support v1 API
20
+
data/README.md CHANGED
@@ -5,7 +5,16 @@
5
5
  This SQL plugin has two parts:
6
6
 
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
- 2. SQL **output** plugin that writes records into RDBMes. An example use case would be aggregating server/app/sensor logs into RDBMS systems.
8
+ 2. SQL **output** plugin that writes records into RDBMSes. An example use case would be aggregating server/app/sensor logs into RDBMS systems.
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 v2's buffer format is different from v1. If you update the plugin to v2, don't reuse v1's buffer.
9
18
 
10
19
  ## Installation
11
20
 
@@ -16,6 +25,13 @@ You should install actual RDBMS driver gem together. `pg` gem for postgresql ada
16
25
 
17
26
  We recommend that mysql2 gem is higher than `0.3.12` and pg gem is higher than `0.16.0`.
18
27
 
28
+ If you use ruby 2.1, use pg gem 0.21.0 (< 1.0.0) because ActiveRecord 5.1.4 or earlier doesn't support Ruby 2.1.
29
+
30
+ ### Resolve tzinfo version conflict
31
+
32
+ If you want to use fluent-plugin-sql with recent fluentd/td-agent, you need to downgrade tzinfo to v1.x manually.
33
+ See also this comment: https://github.com/fluent/fluent-plugin-sql/issues/87#issuecomment-614552292
34
+
19
35
  ## Input: How It Works
20
36
 
21
37
  This plugin runs following SQL periodically:
@@ -139,10 +155,12 @@ This plugin takes advantage of ActiveRecord underneath. For `host`, `port`, `dat
139
155
  * **username** RDBMS login user name
140
156
  * **password** RDBMS login password
141
157
  * **socket** RDBMS socket path
158
+ * **pool** A connection pool synchronizes thread access to a limited number of database connections
159
+ * **timeout** RDBMS connection timeout
142
160
  * **remove_tag_prefix** remove the given prefix from the events. See "tag_prefix" in "Input: Configuration". (optional)
143
161
 
144
162
  \<table\> sections:
145
163
 
146
164
  * **table** RDBM table name
147
165
  * **column_mapping**: [Required] Record to table schema mapping. The format is consists of `from:to` or `key` values are separated by `,`. For example, if set 'item_id:id,item_text:data,updated_at' to **column_mapping**, `item_id` field of record is stored into `id` column and `updated_at` field of record is stored into `updated_at` column.
148
- * **<table pattern>**: the pattern to which the incoming event's tag (after it goes through `remove_tag_prefix`, if given). The patterns should follow the same syntax as [that of <match>](http://docs.fluentd.org/articles/config-file#match-pattern-how-you-control-the-event-flow-inside-fluentd). **Exactly one <table> element must NOT have this parameter so that it becomes the default table to store data**.
166
+ * **\<table pattern\>**: the pattern to which the incoming event's tag (after it goes through `remove_tag_prefix`, if given). The patterns should follow the same syntax as [that of \<match\>](https://docs.fluentd.org/configuration/config-file#how-match-patterns-work). **Exactly one \<table\> element must NOT have this parameter so that it becomes the default table to store data**.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.6.1
1
+ 2.1.0
@@ -4,12 +4,11 @@ $:.push File.expand_path('../lib', __FILE__)
4
4
  Gem::Specification.new do |gem|
5
5
  gem.name = "fluent-plugin-sql"
6
6
  gem.description = "SQL input/output plugin for Fluentd event collector"
7
- gem.homepage = "https://github.com/frsyuki/fluent-plugin-sql"
7
+ gem.homepage = "https://github.com/fluent/fluent-plugin-sql"
8
8
  gem.summary = gem.description
9
9
  gem.version = File.read("VERSION").strip
10
10
  gem.authors = ["Sadayuki Furuhashi"]
11
11
  gem.email = "frsyuki@gmail.com"
12
- gem.has_rdoc = false
13
12
  #gem.platform = Gem::Platform::RUBY
14
13
  gem.files = `git ls-files`.split("\n")
15
14
  gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
@@ -18,11 +17,11 @@ Gem::Specification.new do |gem|
18
17
  gem.license = "Apache-2.0"
19
18
 
20
19
  gem.add_dependency "fluentd", [">= 0.12.17", "< 2"]
21
- gem.add_dependency 'activerecord', "~> 4.2"
20
+ gem.add_dependency 'activerecord', "~> 5.1"
22
21
  gem.add_dependency 'activerecord-import', "~> 0.7"
23
22
  gem.add_development_dependency "rake", ">= 0.9.2"
24
- gem.add_development_dependency "test-unit", "~> 3.1.0"
23
+ gem.add_development_dependency "test-unit", "> 3.1.0"
25
24
  gem.add_development_dependency "test-unit-rr"
26
25
  gem.add_development_dependency "test-unit-notify"
27
- gem.add_development_dependency "pg"
26
+ gem.add_development_dependency "pg", '~> 1.0'
28
27
  end
@@ -16,67 +16,60 @@
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, :default => nil
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, :default => nil
37
+ config_param :username, :string, default: nil
46
38
  desc 'RDBMS login password'
47
- config_param :password, :string, :default => nil, :secret => true
39
+ config_param :password, :string, default: nil, secret: true
48
40
  desc 'RDBMS socket path'
49
- config_param :socket, :string, :default => nil
41
+ config_param :socket, :string, default: nil
42
+ desc 'PostgreSQL schema search path'
43
+ config_param :schema_search_path, :string, default: nil
50
44
 
51
45
  desc 'path to a file to store last rows'
52
- config_param :state_file, :string, :default => nil
46
+ config_param :state_file, :string, default: nil
53
47
  desc 'prefix of tags of events. actual tag will be this_tag_prefix.tables_tag (optional)'
54
- config_param :tag_prefix, :string, :default => nil
48
+ config_param :tag_prefix, :string, default: nil
55
49
  desc 'interval to run SQLs (optional)'
56
- config_param :select_interval, :time, :default => 60
50
+ config_param :select_interval, :time, default: 60
57
51
  desc 'limit of number of rows for each SQL(optional)'
58
- config_param :select_limit, :time, :default => 500
59
-
60
- unless method_defined?(:log)
61
- define_method(:log) { $log }
62
- end
52
+ config_param :select_limit, :time, default: 500
63
53
 
64
54
  class TableElement
65
- include Configurable
55
+ include Fluent::Configurable
66
56
 
67
57
  config_param :table, :string
68
- config_param :tag, :string, :default => nil
69
- config_param :update_column, :string, :default => nil
70
- config_param :time_column, :string, :default => nil
71
- config_param :primary_key, :string, :default => nil
58
+ config_param :tag, :string, default: nil
59
+ config_param :update_column, :string, default: nil
60
+ config_param :time_column, :string, default: nil
61
+ config_param :primary_key, :string, default: nil
62
+
63
+ attr_reader :log
72
64
 
73
65
  def configure(conf)
74
66
  super
75
67
  end
76
68
 
77
- def init(tag_prefix, base_model, router)
69
+ def init(tag_prefix, base_model, router, log)
78
70
  @router = router
79
71
  @tag = "#{tag_prefix}.#{@tag}" if tag_prefix
72
+ @log = log
80
73
 
81
74
  # creates a model for this table
82
75
  table_name = @table
@@ -118,6 +111,17 @@ module Fluent
118
111
  end
119
112
  end
120
113
 
114
+ # Make sure we always have a Fluent::EventTime object regardless of what comes in
115
+ def normalized_time(tv, now)
116
+ return Fluent::EventTime.from_time(tv) if tv.is_a?(Time)
117
+ begin
118
+ Fluent::EventTime.parse(tv.to_s)
119
+ rescue
120
+ log.warn "Message contains invalid timestamp, using current time instead (#{now.inspect})"
121
+ now
122
+ end
123
+ end
124
+
121
125
  # emits next records and returns the last record of emitted records
122
126
  def emit_next_records(last_record, limit)
123
127
  relation = @model
@@ -127,21 +131,19 @@ module Fluent
127
131
  relation = relation.order("#{@update_column} ASC")
128
132
  relation = relation.limit(limit) if limit > 0
129
133
 
130
- now = Engine.now
134
+ now = Fluent::Engine.now
131
135
 
132
- me = MultiEventStream.new
136
+ me = Fluent::MultiEventStream.new
133
137
  relation.each do |obj|
134
138
  record = obj.serializable_hash rescue nil
135
139
  if record
136
- if @time_column && tv = obj.read_attribute(@time_column)
137
- if tv.is_a?(Time)
138
- time = tv.to_i
140
+ time =
141
+ if @time_column && (tv = obj.read_attribute(@time_column))
142
+ normalized_time(tv, now)
139
143
  else
140
- time = Time.parse(tv.to_s).to_i rescue now
144
+ now
141
145
  end
142
- else
143
- time = now
144
- end
146
+
145
147
  me.add(time, record)
146
148
  last_record = record
147
149
  end
@@ -181,13 +183,14 @@ module Fluent
181
183
  @state_store = @state_file.nil? ? MemoryStateStore.new : StateStore.new(@state_file)
182
184
 
183
185
  config = {
184
- :adapter => @adapter,
185
- :host => @host,
186
- :port => @port,
187
- :database => @database,
188
- :username => @username,
189
- :password => @password,
190
- :socket => @socket,
186
+ adapter: @adapter,
187
+ host: @host,
188
+ port: @port,
189
+ database: @database,
190
+ username: @username,
191
+ password: @password,
192
+ socket: @socket,
193
+ schema_search_path: @schema_search_path,
191
194
  }
192
195
 
193
196
  # creates subclass of ActiveRecord::Base so that it can have different
@@ -226,11 +229,11 @@ module Fluent
226
229
  # ignore tables if TableElement#init failed
227
230
  @tables.reject! do |te|
228
231
  begin
229
- te.init(@tag_prefix, @base_model, router)
232
+ te.init(@tag_prefix, @base_model, router, log)
230
233
  log.info "Selecting '#{te.table}' table"
231
234
  false
232
235
  rescue => e
233
- log.warn "Can't handle '#{te.table}' table. Ignoring.", :error => e.message, :error_class => e.class
236
+ log.warn "Can't handle '#{te.table}' table. Ignoring.", error: e
234
237
  log.warn_backtrace e.backtrace
235
238
  true
236
239
  end
@@ -264,7 +267,7 @@ module Fluent
264
267
  @state_store.last_records[t.table] = t.emit_next_records(last_record, @select_limit)
265
268
  @state_store.update!
266
269
  rescue => e
267
- log.error "unexpected error", :error => e.message, :error_class => e.class
270
+ log.error "unexpected error", error: e
268
271
  log.error_backtrace e.backtrace
269
272
  end
270
273
  end
@@ -1,59 +1,59 @@
1
- require "fluent/output"
1
+ require "fluent/plugin/output"
2
2
 
3
- module Fluent
4
- class SQLOutput < BufferedOutput
5
- Plugin.register_output('sql', self)
3
+ require 'active_record'
4
+ require 'activerecord-import'
6
5
 
7
- include SetTimeKeyMixin
8
- include SetTagKeyMixin
6
+ module Fluent::Plugin
7
+ class SQLOutput < Output
8
+ Fluent::Plugin.register_output('sql', self)
9
9
 
10
- # For fluentd v0.12.16 or earlier
11
- class << self
12
- unless method_defined?(:desc)
13
- def desc(description)
14
- end
15
- end
16
- end
10
+ helpers :inject, :compat_parameters, :event_emitter
17
11
 
18
12
  desc 'RDBMS host'
19
13
  config_param :host, :string
20
14
  desc 'RDBMS port'
21
- config_param :port, :integer, :default => nil
15
+ config_param :port, :integer, default: nil
22
16
  desc 'RDBMS driver name.'
23
17
  config_param :adapter, :string
24
18
  desc 'RDBMS login user name'
25
- config_param :username, :string, :default => nil
19
+ config_param :username, :string, default: nil
26
20
  desc 'RDBMS login password'
27
- config_param :password, :string, :default => nil, :secret => true
21
+ config_param :password, :string, default: nil, secret: true
28
22
  desc 'RDBMS database name'
29
23
  config_param :database, :string
30
24
  desc 'RDBMS socket path'
31
- config_param :socket, :string, :default => nil
25
+ config_param :socket, :string, default: nil
26
+ desc 'PostgreSQL schema search path'
27
+ config_param :schema_search_path, :string, default: nil
32
28
  desc 'remove the given prefix from the events'
33
- config_param :remove_tag_prefix, :string, :default => nil
29
+ config_param :remove_tag_prefix, :string, default: nil
34
30
  desc 'enable fallback'
35
- config_param :enable_fallback, :bool, :default => true
31
+ config_param :enable_fallback, :bool, default: true
32
+ desc "size of ActiveRecord's connection pool"
33
+ config_param :pool, :integer, default: 5
34
+ desc "specifies the timeout to establish a new connection to the database before failing"
35
+ config_param :timeout, :integer, default: 5000
36
+
37
+ config_section :buffer do
38
+ config_set_default :chunk_keys, ["tag"]
39
+ end
36
40
 
37
41
  attr_accessor :tables
38
42
 
39
- unless method_defined?(:log)
40
- define_method(:log) { $log }
41
- end
42
-
43
43
  # TODO: Merge SQLInput's TableElement
44
44
  class TableElement
45
- include Configurable
45
+ include Fluent::Configurable
46
46
 
47
47
  config_param :table, :string
48
48
  config_param :column_mapping, :string
49
- config_param :num_retries, :integer, :default => 5
49
+ config_param :num_retries, :integer, default: 5
50
50
 
51
51
  attr_reader :model
52
52
  attr_reader :pattern
53
53
 
54
54
  def initialize(pattern, log, enable_fallback)
55
55
  super()
56
- @pattern = MatchPattern.create(pattern)
56
+ @pattern = Fluent::MatchPattern.create(pattern)
57
57
  @log = log
58
58
  @enable_fallback = enable_fallback
59
59
  end
@@ -88,14 +88,15 @@ module Fluent
88
88
  # @model.column_names
89
89
  end
90
90
 
91
- def import(chunk)
91
+ def import(chunk, output)
92
+ tag = chunk.metadata.tag
92
93
  records = []
93
- chunk.msgpack_each { |tag, time, data|
94
+ chunk.msgpack_each { |time, data|
94
95
  begin
95
- # format process should be moved to emit / format after supports error stream.
96
+ data = output.inject_values_to_record(tag, time, data)
96
97
  records << @model.new(@format_proc.call(data))
97
98
  rescue => e
98
- args = {:error => e.message, :error_class => e.class, :table => @table, :record => Yajl.dump(data)}
99
+ args = {error: e, table: @table, record: Yajl.dump(data)}
99
100
  @log.warn "Failed to create the model. Ignore a record:", args
100
101
  end
101
102
  }
@@ -104,10 +105,10 @@ module Fluent
104
105
  rescue ActiveRecord::StatementInvalid, ActiveRecord::Import::MissingColumnError => e
105
106
  if @enable_fallback
106
107
  # ignore other exceptions to use Fluentd retry mechanizm
107
- @log.warn "Got deterministic error. Fallback to one-by-one import", :error => e.message, :error_class => e.class
108
+ @log.warn "Got deterministic error. Fallback to one-by-one import", error: e
108
109
  one_by_one_import(records)
109
110
  else
110
- $log.warn "Got deterministic error. Fallback is disabled", :error => e.message, :error_class => e.class
111
+ @log.warn "Got deterministic error. Fallback is disabled", error: e
111
112
  raise e
112
113
  end
113
114
  end
@@ -119,15 +120,15 @@ module Fluent
119
120
  begin
120
121
  @model.import([record])
121
122
  rescue ActiveRecord::StatementInvalid, ActiveRecord::Import::MissingColumnError => e
122
- @log.error "Got deterministic error again. Dump a record", :error => e.message, :error_class => e.class, :record => record
123
+ @log.error "Got deterministic error again. Dump a record", error: e, record: record
123
124
  rescue => e
124
125
  retries += 1
125
126
  if retries > @num_retries
126
- @log.error "Can't recover undeterministic error. Dump a record", :error => e.message, :error_class => e.class, :record => record
127
+ @log.error "Can't recover undeterministic error. Dump a record", error: e, record: record
127
128
  next
128
129
  end
129
130
 
130
- @log.warn "Failed to import a record: retry number = #{retries}", :error => e.message, :error_class => e.class
131
+ @log.warn "Failed to import a record: retry number = #{retries}", error: e
131
132
  sleep 0.5
132
133
  retry
133
134
  end
@@ -149,11 +150,11 @@ module Fluent
149
150
 
150
151
  def initialize
151
152
  super
152
- require 'active_record'
153
- require 'activerecord-import'
154
153
  end
155
154
 
156
155
  def configure(conf)
156
+ compat_parameters_convert(conf, :inject, :buffer)
157
+
157
158
  super
158
159
 
159
160
  if remove_tag_prefix = conf['remove_tag_prefix']
@@ -174,10 +175,13 @@ module Fluent
174
175
  @tables << te
175
176
  end
176
177
  }
177
- @only_default = @tables.empty?
178
+
179
+ if @pool < @buffer_config.flush_thread_count
180
+ log.warn "connection pool size is smaller than buffer's flush_thread_count. Recommend to increase pool value", :pool => @pool, :flush_thread_count => @buffer_config.flush_thread_count
181
+ end
178
182
 
179
183
  if @default_table.nil?
180
- raise ConfigError, "There is no default table. <table> is required in sql output"
184
+ raise Fluent::ConfigError, "There is no default table. <table> is required in sql output"
181
185
  end
182
186
  end
183
187
 
@@ -185,13 +189,16 @@ module Fluent
185
189
  super
186
190
 
187
191
  config = {
188
- :adapter => @adapter,
189
- :host => @host,
190
- :port => @port,
191
- :database => @database,
192
- :username => @username,
193
- :password => @password,
194
- :socket => @socket,
192
+ adapter: @adapter,
193
+ host: @host,
194
+ port: @port,
195
+ database: @database,
196
+ username: @username,
197
+ password: @password,
198
+ socket: @socket,
199
+ schema_search_path: @schema_search_path,
200
+ pool: @pool,
201
+ timeout: @timeout,
195
202
  }
196
203
 
197
204
  @base_model = Class.new(ActiveRecord::Base) do
@@ -212,27 +219,20 @@ module Fluent
212
219
  super
213
220
  end
214
221
 
215
- def emit(tag, es, chain)
216
- if @only_default
217
- super(tag, es, chain)
218
- else
219
- super(tag, es, chain, format_tag(tag))
220
- end
221
- end
222
-
223
- def format(tag, time, record)
224
- [tag, time, record].to_msgpack
222
+ def formatted_to_msgpack_binary
223
+ true
225
224
  end
226
225
 
227
226
  def write(chunk)
228
227
  ActiveRecord::Base.connection_pool.with_connection do
229
228
 
230
229
  @tables.each { |table|
231
- if table.pattern.match(chunk.key)
232
- return table.import(chunk)
230
+ tag = format_tag(chunk.metadata.tag)
231
+ if table.pattern.match(tag)
232
+ return table.import(chunk, self)
233
233
  end
234
234
  }
235
- @default_table.import(chunk)
235
+ @default_table.import(chunk, self)
236
236
  end
237
237
  end
238
238
 
@@ -244,14 +244,14 @@ 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.", :error => e.message, :error_class => e.class
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
251
251
  end
252
252
 
253
253
  def format_tag(tag)
254
- if @remove_tag_prefix
254
+ if tag && @remove_tag_prefix
255
255
  tag.gsub(@remove_tag_prefix, '')
256
256
  else
257
257
  tag
@@ -20,5 +20,12 @@ ActiveRecord::Schema.define(version: 20160225030107) do
20
20
  t.datetime "created_at", null: false
21
21
  t.datetime "updated_at", null: false
22
22
  end
23
+
24
+ create_table "messages_custom_time", force: :cascade do |t|
25
+ t.string "message"
26
+ t.datetime "created_at", null: false
27
+ t.datetime "updated_at", null: false
28
+ t.string "custom_time"
29
+ end
23
30
  end
24
31
 
@@ -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
@@ -17,6 +18,8 @@ class SqlInputTest < Test::Unit::TestCase
17
18
  username fluentd
18
19
  password fluentd
19
20
 
21
+ schema_search_path public
22
+
20
23
  tag_prefix db
21
24
 
22
25
  <table>
@@ -28,7 +31,7 @@ class SqlInputTest < Test::Unit::TestCase
28
31
  ]
29
32
 
30
33
  def create_driver(conf = CONFIG)
31
- Fluent::Test::InputTestDriver.new(Fluent::SQLInput).configure(conf)
34
+ Fluent::Test::Driver::Input.new(Fluent::Plugin::SQLInput).configure(conf)
32
35
  end
33
36
 
34
37
  def test_configure
@@ -40,6 +43,7 @@ class SqlInputTest < Test::Unit::TestCase
40
43
  database: "fluentd_test",
41
44
  username: "fluentd",
42
45
  password: "fluentd",
46
+ schema_search_path: "public",
43
47
  tag_prefix: "db"
44
48
  }
45
49
  actual = {
@@ -49,6 +53,7 @@ class SqlInputTest < Test::Unit::TestCase
49
53
  database: d.instance.database,
50
54
  username: d.instance.username,
51
55
  password: d.instance.password,
56
+ schema_search_path: d.instance.schema_search_path,
52
57
  tag_prefix: d.instance.tag_prefix
53
58
  }
54
59
  assert_equal(expected, actual)
@@ -65,18 +70,21 @@ class SqlInputTest < Test::Unit::TestCase
65
70
  Message.create!(message: "message 2")
66
71
  Message.create!(message: "message 3")
67
72
 
73
+ d.end_if do
74
+ d.record_count >= 3
75
+ end
68
76
  d.run
69
77
 
70
- assert_equal("db.logs", d.emits[0][0])
78
+ assert_equal("db.logs", d.events[0][0])
71
79
  expected = [
72
- [d.emits[0][1], "message 1"],
73
- [d.emits[1][1], "message 2"],
74
- [d.emits[2][1], "message 3"],
80
+ [d.events[0][1], "message 1"],
81
+ [d.events[1][1], "message 2"],
82
+ [d.events[2][1], "message 3"],
75
83
  ]
76
84
  actual = [
77
- [Time.parse(d.emits[0][2]["updated_at"]).to_i, d.emits[0][2]["message"]],
78
- [Time.parse(d.emits[1][2]["updated_at"]).to_i, d.emits[1][2]["message"]],
79
- [Time.parse(d.emits[2][2]["updated_at"]).to_i, d.emits[2][2]["message"]],
85
+ [Fluent::EventTime.parse(d.events[0][2]["updated_at"]), d.events[0][2]["message"]],
86
+ [Fluent::EventTime.parse(d.events[1][2]["updated_at"]), d.events[1][2]["message"]],
87
+ [Fluent::EventTime.parse(d.events[2][2]["updated_at"]), d.events[2][2]["message"]],
80
88
  ]
81
89
  assert_equal(expected, actual)
82
90
  end
@@ -0,0 +1,114 @@
1
+ require "helper"
2
+ require "fluent/test/driver/input"
3
+
4
+ class SqlInputCustomTimeTest < Test::Unit::TestCase
5
+ def setup
6
+ Fluent::Test.setup
7
+ end
8
+
9
+ def teardown
10
+ end
11
+
12
+ CONFIG = %[
13
+ adapter postgresql
14
+ host localhost
15
+ port 5432
16
+ database fluentd_test
17
+
18
+ username fluentd
19
+ password fluentd
20
+
21
+ schema_search_path public
22
+
23
+ tag_prefix db
24
+
25
+ <table>
26
+ table messages_custom_time
27
+ tag logs
28
+ update_column updated_at
29
+ time_column custom_time
30
+ </table>
31
+ ]
32
+
33
+ def create_driver(conf = CONFIG)
34
+ Fluent::Test::Driver::Input.new(Fluent::Plugin::SQLInput).configure(conf)
35
+ end
36
+
37
+ def test_configure
38
+ d = create_driver
39
+ expected = {
40
+ host: "localhost",
41
+ port: 5432,
42
+ adapter: "postgresql",
43
+ database: "fluentd_test",
44
+ username: "fluentd",
45
+ password: "fluentd",
46
+ schema_search_path: "public",
47
+ tag_prefix: "db"
48
+ }
49
+ actual = {
50
+ host: d.instance.host,
51
+ port: d.instance.port,
52
+ adapter: d.instance.adapter,
53
+ database: d.instance.database,
54
+ username: d.instance.username,
55
+ password: d.instance.password,
56
+ schema_search_path: d.instance.schema_search_path,
57
+ tag_prefix: d.instance.tag_prefix
58
+ }
59
+ assert_equal(expected, actual)
60
+ tables = d.instance.instance_variable_get(:@tables)
61
+ assert_equal(1, tables.size)
62
+ messages_custom_time = tables.first
63
+ assert_equal("messages_custom_time", messages_custom_time.table)
64
+ assert_equal("logs", messages_custom_time.tag)
65
+ end
66
+
67
+ def test_message
68
+ d = create_driver(CONFIG + "select_interval 1")
69
+
70
+ start_time = Fluent::EventTime.now
71
+
72
+ # Create one message with a valid timestamp containing milliseconds and a time zone
73
+ Message.create!(message: "message 1", custom_time: '2020-08-27 15:00:16.100758000 -0400')
74
+
75
+ # Create one message without a timestamp so that we can test auto-creation
76
+ Message.create!(message: "message 2 (no timestamp)", custom_time: nil)
77
+
78
+ # Create one message with an unparseable timestamp so that we can check that a valid
79
+ # one is auto-generated.
80
+ Message.create!(message: "message 3 (bad timestamp)", custom_time: 'foo')
81
+
82
+ d.end_if do
83
+ d.record_count >= 3
84
+ end
85
+ d.run(timeout: 5)
86
+
87
+ assert_equal("db.logs", d.events[0][0])
88
+ expected = [
89
+ [d.events[0][1], "message 1"],
90
+ [d.events[1][1], "message 2 (no timestamp)"],
91
+ [d.events[2][1], "message 3 (bad timestamp)"],
92
+ ]
93
+
94
+ actual = [
95
+ [Fluent::EventTime.parse(d.events[0][2]["custom_time"]), d.events[0][2]["message"]],
96
+ d.events[1][2]["message"],
97
+ d.events[2][2]["message"],
98
+ ]
99
+
100
+ assert_equal(expected[0], actual[0])
101
+
102
+ # Messages 2 and 3 should have the same messages but (usually) a slightly later
103
+ # timestamps because they are generated by the input plugin instead of the test
104
+ # code
105
+ [1,2].each do |i|
106
+ assert_equal(expected[i][1], actual[i])
107
+ assert_operator(expected[i][0], :>=, start_time)
108
+ end
109
+ end
110
+
111
+ class Message < ActiveRecord::Base
112
+ self.table_name = "messages_custom_time"
113
+ end
114
+ end
@@ -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
@@ -17,6 +18,8 @@ class SqlOutputTest < Test::Unit::TestCase
17
18
  username fluentd
18
19
  password fluentd
19
20
 
21
+ schema_search_path public
22
+
20
23
  remove_tag_prefix db
21
24
 
22
25
  <table>
@@ -26,7 +29,7 @@ class SqlOutputTest < Test::Unit::TestCase
26
29
  ]
27
30
 
28
31
  def create_driver(conf = CONFIG)
29
- Fluent::Test::BufferedOutputTestDriver.new(Fluent::SQLOutput).configure(conf)
32
+ Fluent::Test::Driver::Output.new(Fluent::Plugin::SQLOutput).configure(conf)
30
33
  end
31
34
 
32
35
  def test_configure
@@ -38,8 +41,10 @@ class SqlOutputTest < Test::Unit::TestCase
38
41
  database: "fluentd_test",
39
42
  username: "fluentd",
40
43
  password: "fluentd",
44
+ schema_search_path: 'public',
41
45
  remove_tag_suffix: /^db/,
42
- enable_fallback: true
46
+ enable_fallback: true,
47
+ pool: 5
43
48
  }
44
49
  actual = {
45
50
  host: d.instance.host,
@@ -48,8 +53,10 @@ class SqlOutputTest < Test::Unit::TestCase
48
53
  database: d.instance.database,
49
54
  username: d.instance.username,
50
55
  password: d.instance.password,
56
+ schema_search_path: d.instance.schema_search_path,
51
57
  remove_tag_suffix: d.instance.remove_tag_prefix,
52
- enable_fallback: d.instance.enable_fallback
58
+ enable_fallback: d.instance.enable_fallback,
59
+ pool: d.instance.pool
53
60
  }
54
61
  assert_equal(expected, actual)
55
62
  assert_empty(d.instance.tables)
@@ -61,10 +68,10 @@ class SqlOutputTest < Test::Unit::TestCase
61
68
  d = create_driver
62
69
  time = Time.parse("2011-01-02 13:14:15 UTC").to_i
63
70
 
64
- d.emit({"message" => "message1"}, time)
65
- d.emit({"message" => "message2"}, time)
66
-
67
- d.run
71
+ d.run(default_tag: 'test') do
72
+ d.feed(time, {"message" => "message1"})
73
+ d.feed(time, {"message" => "message2"})
74
+ end
68
75
 
69
76
  default_table = d.instance.instance_variable_get(:@default_table)
70
77
  model = default_table.instance_variable_get(:@model)
@@ -78,10 +85,10 @@ class SqlOutputTest < Test::Unit::TestCase
78
85
  d = create_driver
79
86
  time = Time.parse("2011-01-02 13:14:15 UTC").to_i
80
87
 
81
- d.emit({"message" => "message1"}, time)
82
- d.emit({"message" => "message2"}, time)
88
+ d.run(default_tag: 'test') do
89
+ d.feed(time, {"message" => "message1"})
90
+ d.feed(time, {"message" => "message2"})
83
91
 
84
- d.run do
85
92
  default_table = d.instance.instance_variable_get(:@default_table)
86
93
  model = default_table.instance_variable_get(:@model)
87
94
  mock(model).import(anything).at_least(1) do
@@ -95,10 +102,10 @@ class SqlOutputTest < Test::Unit::TestCase
95
102
  d = create_driver
96
103
  time = Time.parse("2011-01-02 13:14:15 UTC").to_i
97
104
 
98
- d.emit({"message" => "message1"}, time)
99
- d.emit({"message" => "message2"}, time)
105
+ d.run(default_tag: 'test') do
106
+ d.feed(time, {"message" => "message1"})
107
+ d.feed(time, {"message" => "message2"})
100
108
 
101
- d.run do
102
109
  default_table = d.instance.instance_variable_get(:@default_table)
103
110
  model = default_table.instance_variable_get(:@model)
104
111
  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.6.1
4
+ version: 2.1.0
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-06-27 00:00:00.000000000 Z
11
+ date: 2020-09-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fluentd
@@ -36,14 +36,14 @@ dependencies:
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: '4.2'
39
+ version: '5.1'
40
40
  type: :runtime
41
41
  prerelease: false
42
42
  version_requirements: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: '4.2'
46
+ version: '5.1'
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: activerecord-import
49
49
  requirement: !ruby/object:Gem::Requirement
@@ -76,14 +76,14 @@ dependencies:
76
76
  name: test-unit
77
77
  requirement: !ruby/object:Gem::Requirement
78
78
  requirements:
79
- - - "~>"
79
+ - - ">"
80
80
  - !ruby/object:Gem::Version
81
81
  version: 3.1.0
82
82
  type: :development
83
83
  prerelease: false
84
84
  version_requirements: !ruby/object:Gem::Requirement
85
85
  requirements:
86
- - - "~>"
86
+ - - ">"
87
87
  - !ruby/object:Gem::Version
88
88
  version: 3.1.0
89
89
  - !ruby/object:Gem::Dependency
@@ -118,16 +118,16 @@ dependencies:
118
118
  name: pg
119
119
  requirement: !ruby/object:Gem::Requirement
120
120
  requirements:
121
- - - ">="
121
+ - - "~>"
122
122
  - !ruby/object:Gem::Version
123
- version: '0'
123
+ version: '1.0'
124
124
  type: :development
125
125
  prerelease: false
126
126
  version_requirements: !ruby/object:Gem::Requirement
127
127
  requirements:
128
- - - ">="
128
+ - - "~>"
129
129
  - !ruby/object:Gem::Version
130
- version: '0'
130
+ version: '1.0'
131
131
  description: SQL input/output plugin for Fluentd event collector
132
132
  email: frsyuki@gmail.com
133
133
  executables: []
@@ -135,6 +135,7 @@ extensions: []
135
135
  extra_rdoc_files: []
136
136
  files:
137
137
  - ".travis.yml"
138
+ - ChangeLog
138
139
  - Gemfile
139
140
  - Gemfile.v0.12
140
141
  - README.md
@@ -146,8 +147,9 @@ files:
146
147
  - test/fixtures/schema.rb
147
148
  - test/helper.rb
148
149
  - test/plugin/test_in_sql.rb
150
+ - test/plugin/test_in_sql_with_custom_time.rb
149
151
  - test/plugin/test_out_sql.rb
150
- homepage: https://github.com/frsyuki/fluent-plugin-sql
152
+ homepage: https://github.com/fluent/fluent-plugin-sql
151
153
  licenses:
152
154
  - Apache-2.0
153
155
  metadata: {}
@@ -166,8 +168,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
166
168
  - !ruby/object:Gem::Version
167
169
  version: '0'
168
170
  requirements: []
169
- rubyforge_project:
170
- rubygems_version: 2.6.11
171
+ rubygems_version: 3.0.3
171
172
  signing_key:
172
173
  specification_version: 4
173
174
  summary: SQL input/output plugin for Fluentd event collector
@@ -175,4 +176,5 @@ test_files:
175
176
  - test/fixtures/schema.rb
176
177
  - test/helper.rb
177
178
  - test/plugin/test_in_sql.rb
179
+ - test/plugin/test_in_sql_with_custom_time.rb
178
180
  - test/plugin/test_out_sql.rb