fluent-plugin-pgjson 0.0.8 → 0.0.9

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: 1677b7c020f8c8044319c6b2a42c587bc0fb4301
4
- data.tar.gz: 4b5b242dc0cb85fc843ed338fe341fa795220b84
2
+ SHA256:
3
+ metadata.gz: 7196d7c7b7181d2837c5a7e037caaf228335c1cac0b60b8212848d5b354652e3
4
+ data.tar.gz: a5c3f94dd289a27641c40daaa46a665b375afe9023ccbece49f44cd6338ff7fb
5
5
  SHA512:
6
- metadata.gz: e5098cf14816bab706388fd80818c4269cb527da52ab451fc82a972506dc984dcca7697c49ece7bce483fd11762b6ec0a5b3c04b4698173daaadea0c7a7df687
7
- data.tar.gz: bcf9756da26ae5120f90526b659606f1cada84dcd410be0b8bec69f44886a5cb6221068d7232d91aa0044551054dbc0475f7f8193107812140cecf68aff973af
6
+ metadata.gz: adc06d292c08d6bccc5b7ad4471abfbbe585af918044b306249f1c1f7ceb18eb3dd0eabbaedf2ddf04e62c88077d931288437be3faae7a272af731887d628e42
7
+ data.tar.gz: 7f627fdf20323d838f2254f7f64b101e5937e47b6113dd2e380759549195c23f26abf852fffc0679cd925bcfbb126736e486433343887e24f87eab4bd319752b
data/README.md CHANGED
@@ -39,6 +39,12 @@ CREATE TABLE fluentd (
39
39
  );
40
40
  ```
41
41
 
42
+ ### Configurable JSON Encoder
43
+
44
+ Fluentd's standard JSON encoder is `yajl`.
45
+ `yajl` is robust for invalid byte sequence.
46
+ But this plugin's default value is `json` which is Ruby standard JSON encoder for backward compatibility.
47
+
42
48
  ## Configuration
43
49
 
44
50
  ### Example
@@ -73,6 +79,8 @@ CREATE TABLE fluentd (
73
79
  |time_col|column name to insert time|time|
74
80
  |tag_col|column name to insert tag|tag|
75
81
  |record_col|column name to insert record|record|
82
+ |msgpack|use msgpack format for inserting records|false|
83
+ |encoder|choose prefer JSON encoder (yajl/json)|json|
76
84
 
77
85
  ## Copyright
78
86
 
@@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__)
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "fluent-plugin-pgjson"
6
- s.version = "0.0.8"
6
+ s.version = "0.0.9"
7
7
  s.authors = ["OKUNO Akihiro"]
8
8
  s.email = ["choplin.choplin@gmail.com"]
9
9
  s.homepage = "https://github.com/choplin/fluent-plugin-pgjson"
@@ -18,4 +18,6 @@ Gem::Specification.new do |s|
18
18
 
19
19
  s.add_runtime_dependency "fluentd"
20
20
  s.add_runtime_dependency "pg"
21
+ s.add_development_dependency "test-unit", ">= 3.1.0"
22
+ s.add_development_dependency "rake", ">= 11.0"
21
23
  end
@@ -1,8 +1,17 @@
1
- module Fluent
1
+ require 'fluent/plugin/output'
2
+ require 'pg'
3
+ require 'yajl'
4
+ require 'json'
2
5
 
3
- class PgJsonOutput < Fluent::BufferedOutput
6
+ module Fluent::Plugin
7
+
8
+ class PgJsonOutput < Fluent::Output
4
9
  Fluent::Plugin.register_output('pgjson', self)
5
10
 
11
+ helpers :compat_parameters
12
+
13
+ DEFAULT_BUFFER_TYPE = "memory"
14
+
6
15
  config_param :host , :string , :default => 'localhost'
7
16
  config_param :port , :integer , :default => 5432
8
17
  config_param :sslmode , :string , :default => 'prefer'
@@ -14,35 +23,60 @@ class PgJsonOutput < Fluent::BufferedOutput
14
23
  config_param :tag_col , :string , :default => 'tag'
15
24
  config_param :record_col , :string , :default => 'record'
16
25
  config_param :msgpack , :bool , :default => false
26
+ config_param :encoder , :enum, list: [:yajl, :json], :default => :json
27
+ config_param :time_format, :string , :default => '%F %T.%N %z'
28
+
29
+ config_section :buffer do
30
+ config_set_default :@type, DEFAULT_BUFFER_TYPE
31
+ config_set_default :chunk_keys, ['tag']
32
+ end
17
33
 
18
34
  def initialize
19
35
  super
20
- require 'pg'
21
36
  @conn = nil
22
37
  end
23
38
 
24
39
  def configure(conf)
40
+ compat_parameters_convert(conf, :buffer)
25
41
  super
42
+ unless @chunk_key_tag
43
+ raise Fluent::ConfigError, "'tag' in chunk_keys is required."
44
+ end
45
+ @encoder = case @encoder
46
+ when :yajl
47
+ Yajl
48
+ when :json
49
+ JSON
50
+ end
26
51
  end
27
52
 
28
53
  def shutdown
29
- super
30
-
31
54
  if ! @conn.nil? and ! @conn.finished?
32
55
  @conn.close()
33
56
  end
57
+
58
+ super
59
+ end
60
+
61
+ def formatted_to_msgpack_binary
62
+ true
63
+ end
64
+
65
+ def multi_workers_ready?
66
+ true
34
67
  end
35
68
 
36
69
  def format(tag, time, record)
37
- [tag, time, record].to_msgpack
70
+ [Time.at(time).strftime(@time_format), record].to_msgpack
38
71
  end
39
72
 
40
73
  def write(chunk)
41
74
  init_connection
42
75
  @conn.exec("COPY #{@table} (#{@tag_col}, #{@time_col}, #{@record_col}) FROM STDIN WITH DELIMITER E'\\x01'")
43
76
  begin
44
- chunk.msgpack_each do |tag, time, record|
45
- @conn.put_copy_data "#{tag}\x01#{Time.at(time).to_s}\x01#{record_value(record)}\n"
77
+ tag = chunk.metadata.tag
78
+ chunk.msgpack_each do |time, record|
79
+ @conn.put_copy_data "#{tag}\x01#{time}\x01#{record_value(record)}\n"
46
80
  end
47
81
  rescue => err
48
82
  errmsg = "%s while copy data: %s" % [ err.class.name, err.message ]
@@ -62,7 +96,7 @@ class PgJsonOutput < Fluent::BufferedOutput
62
96
  $log.debug "connecting to PostgreSQL server #{@host}:#{@port}, database #{@database}..."
63
97
 
64
98
  begin
65
- @conn = PGconn.new(:dbname => @database, :host => @host, :port => @port, :sslmode => @sslmode, :user => @user, :password => @password)
99
+ @conn = PG::Connection.new(:dbname => @database, :host => @host, :port => @port, :sslmode => @sslmode, :user => @user, :password => @password)
66
100
  rescue
67
101
  if ! @conn.nil?
68
102
  @conn.close()
@@ -77,7 +111,7 @@ class PgJsonOutput < Fluent::BufferedOutput
77
111
  if @msgpack
78
112
  "\\#{@conn.escape_bytea(record.to_msgpack)}"
79
113
  else
80
- json = record.to_json
114
+ json = @encoder.dump(record)
81
115
  json.gsub!(/\\/){ '\\\\' }
82
116
  json
83
117
  end
data/test/helper.rb CHANGED
@@ -1,27 +1,7 @@
1
- require 'rubygems'
2
- require 'bundler'
3
- begin
4
- Bundler.setup(:default, :development)
5
- rescue Bundler::BundlerError => e
6
- $stderr.puts e.message
7
- $stderr.puts "Run `bundle install` to install missing gems"
8
- exit e.status_code
9
- end
1
+ require 'bundler/setup'
10
2
  require 'test/unit'
11
3
 
12
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
13
- $LOAD_PATH.unshift(File.dirname(__FILE__))
4
+ $LOAD_PATH.unshift(File.join(__dir__, '..', 'lib'))
5
+ $LOAD_PATH.unshift(__dir__)
14
6
  require 'fluent/test'
15
- unless ENV.has_key?('VERBOSE')
16
- nulllogger = Object.new
17
- nulllogger.instance_eval {|obj|
18
- def method_missing(method, *args)
19
- # pass
20
- end
21
- }
22
- $log = nulllogger
23
- end
24
7
  require 'fluent/plugin/out_pgjson'
25
-
26
- class Test::Unit::TestCase
27
- end
@@ -1,18 +1,23 @@
1
1
  require 'pg'
2
2
  require 'securerandom'
3
3
  require 'helper'
4
+ require 'fluent/test/driver/output'
5
+ require 'fluent/test/helpers'
4
6
 
5
7
  class PgJsonOutputTest < Test::Unit::TestCase
8
+ include Fluent::Test::Helpers
9
+
6
10
  HOST = "localhost"
7
11
  PORT = 5432
8
12
  DATABASE = "postgres"
9
13
  TABLE = "test_fluentd_#{SecureRandom.hex}"
10
- USER = "postgres"
11
- PASSWORD = "postgres"
14
+ USER = ENV["PSQL_USER"] || "postgres"
15
+ PASSWORD = ENV["PSQL_PASSWORD"] || "postgres"
12
16
 
13
17
  TIME_COL = "time"
14
18
  TAG_COL = "tag"
15
19
  RECORD_COL = "record"
20
+ ENCODER = JSON
16
21
 
17
22
  CONFIG = %[
18
23
  type pgjson
@@ -31,8 +36,8 @@ class PgJsonOutputTest < Test::Unit::TestCase
31
36
  Fluent::Test.setup
32
37
  end
33
38
 
34
- def create_driver(conf = CONFIG, tag = 'test')
35
- Fluent::Test::BufferedOutputTestDriver.new(Fluent::PgJsonOutput).configure(conf)
39
+ def create_driver(conf = CONFIG)
40
+ Fluent::Test::Driver::Output.new(Fluent::Plugin::PgJsonOutput).configure(conf)
36
41
  end
37
42
 
38
43
  def test_configure
@@ -47,22 +52,46 @@ class PgJsonOutputTest < Test::Unit::TestCase
47
52
  assert_equal TIME_COL, d.instance.time_col
48
53
  assert_equal TAG_COL, d.instance.tag_col
49
54
  assert_equal RECORD_COL, d.instance.record_col
55
+ assert_equal ENCODER, d.instance.encoder
56
+ end
57
+
58
+ def test_invalid_chunk_keys
59
+ assert_raise_message(/'tag' in chunk_keys is required./) do
60
+ create_driver(Fluent::Config::Element.new(
61
+ 'ROOT', '', {
62
+ '@type' => 'pgjson',
63
+ 'host' => "#{HOST}",
64
+ 'port' => "#{PORT}",
65
+ 'database' => "#{DATABASE}",
66
+ 'table' => "#{TABLE}",
67
+ 'user' => "#{USER}",
68
+ 'password' => "#{PASSWORD}",
69
+ 'time_col' => "#{TIME_COL}",
70
+ 'tag_col' => "#{TAG_COL}",
71
+ 'record_col' => "#{RECORD_COL}",
72
+ }, [
73
+ Fluent::Config::Element.new('buffer', 'mykey', {
74
+ 'chunk_keys' => 'mykey'
75
+ }, [])
76
+ ]))
77
+ end
50
78
  end
51
79
 
52
80
  def test_write
53
81
  with_connection do |conn|
54
82
  tag = 'test'
55
- time = Time.parse("2014-12-26 07:58:37 UTC")
83
+ time = event_time("2014-12-26 07:58:37 UTC")
56
84
  record = {"a"=>1}
57
85
 
58
- d = create_driver(CONFIG, tag)
59
- d.emit(record, time.to_i)
60
- d.run
86
+ d = create_driver(CONFIG)
87
+ d.run(default_tag: tag) do
88
+ d.feed(time, record)
89
+ end
61
90
  wait_for_data(conn)
62
91
 
63
92
  res = conn.exec("select * from #{TABLE}")[0]
64
93
  assert_equal res[TAG_COL], tag
65
- assert_equal Time.parse(res[TIME_COL]), time
94
+ assert_equal event_time(res[TIME_COL]), time
66
95
  assert_equal res[RECORD_COL], record.to_json
67
96
  end
68
97
  end
@@ -70,17 +99,18 @@ class PgJsonOutputTest < Test::Unit::TestCase
70
99
  def test_escape_of_backslash
71
100
  with_connection do |conn|
72
101
  tag = 'test'
73
- time = Time.parse("2014-12-26 07:58:37 UTC")
102
+ time = event_time("2014-12-26 07:58:37 UTC")
74
103
  record = {"a"=>"\"foo\""}
75
104
 
76
- d = create_driver(CONFIG, tag)
77
- d.emit(record, time.to_i)
78
- d.run
105
+ d = create_driver(CONFIG)
106
+ d.run(default_tag: tag) do
107
+ d.feed(time, record)
108
+ end
79
109
  wait_for_data(conn)
80
110
 
81
111
  res = conn.exec("select * from #{TABLE}")[0]
82
112
  assert_equal res[TAG_COL], tag
83
- assert_equal Time.parse(res[TIME_COL]), time
113
+ assert_equal event_time(res[TIME_COL]), time
84
114
  assert_equal res[RECORD_COL], record.to_json
85
115
  end
86
116
  end
@@ -88,17 +118,18 @@ class PgJsonOutputTest < Test::Unit::TestCase
88
118
  def test_invalid_json
89
119
  with_connection do |conn|
90
120
  tag = 'test'
91
- time = Time.parse("2014-12-26 07:58:37 UTC")
121
+ time = event_time("2014-12-26 07:58:37 UTC")
92
122
 
93
- d = create_driver(CONFIG, tag)
123
+ d = create_driver(CONFIG)
94
124
  instance = d.instance
95
125
  def instance.record_value(record)
96
126
  'invalid json'
97
127
  end
98
- d.emit('', time.to_i)
99
128
 
100
129
  assert_raise RuntimeError do
101
- d.run
130
+ d.run(default_tag: tag) do
131
+ d.feed(time, {})
132
+ end
102
133
  end
103
134
  end
104
135
  end
@@ -108,7 +139,7 @@ class PgJsonOutputTest < Test::Unit::TestCase
108
139
  conn = nil
109
140
 
110
141
  assert_nothing_raised do
111
- conn = PGconn.new(:dbname => DATABASE, :host => HOST, :port => PORT, :user => USER, :password => PASSWORD)
142
+ conn = PG::Connection.new(:dbname => DATABASE, :host => HOST, :port => PORT, :user => USER, :password => PASSWORD)
112
143
  end
113
144
 
114
145
  conn
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluent-plugin-pgjson
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 0.0.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - OKUNO Akihiro
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-09-01 00:00:00.000000000 Z
11
+ date: 2018-06-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fluentd
@@ -38,6 +38,34 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: test-unit
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 3.1.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 3.1.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '11.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '11.0'
41
69
  description: ''
42
70
  email:
43
71
  - choplin.choplin@gmail.com
@@ -75,7 +103,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
75
103
  version: '0'
76
104
  requirements: []
77
105
  rubyforge_project:
78
- rubygems_version: 2.4.5
106
+ rubygems_version: 2.7.6
79
107
  signing_key:
80
108
  specification_version: 4
81
109
  summary: ''