fluent-plugin-pgjson 0.0.8 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
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: ''