fluent-plugin-pghstore 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +11 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +13 -0
- data/README.rdoc +76 -0
- data/Rakefile +12 -0
- data/example.conf +13 -0
- data/fluent-plugin-pghstore.gemspec +27 -0
- data/lib/fluent/plugin/out_pghstore.rb +102 -0
- data/test/helper.rb +28 -0
- data/test/plugin/test_out_pghstore.rb +44 -0
- data/test/plugin/test_out_pghstore.rb.bak +277 -0
- metadata +136 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright (c) 2012- Shirou WAKAYAMA
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
data/README.rdoc
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
= fluent-plugin-pghstore
|
2
|
+
|
3
|
+
== Component
|
4
|
+
|
5
|
+
=== PgHStoreOutput
|
6
|
+
|
7
|
+
Output to PostgreSQL hstore database.
|
8
|
+
|
9
|
+
Output table should have tag, time and record column.:
|
10
|
+
|
11
|
+
CREATE TABLE #{tablename} (
|
12
|
+
tag TEXT,
|
13
|
+
time TIMESTAMP WITH TIME ZONE,
|
14
|
+
record HSTORE
|
15
|
+
);
|
16
|
+
|
17
|
+
== Requirement
|
18
|
+
|
19
|
+
- PostgreSQL 9.0 or higher
|
20
|
+
- hstore changed at PostgreSQL 9.0.
|
21
|
+
- postgres-contrib
|
22
|
+
- ruby-pg
|
23
|
+
|
24
|
+
=== How to Install hstore
|
25
|
+
|
26
|
+
hstore is in the contrib.
|
27
|
+
|
28
|
+
9.1 or higher:
|
29
|
+
|
30
|
+
psql <dbname> -c "CREATE EXTENSION hstore;"
|
31
|
+
|
32
|
+
9.0:
|
33
|
+
|
34
|
+
psql <dbname> -f ${PGHOME}/share/contrib/hstore.sql
|
35
|
+
|
36
|
+
== Configuration
|
37
|
+
|
38
|
+
Example:
|
39
|
+
|
40
|
+
<match apache.*>
|
41
|
+
type pghstore
|
42
|
+
database test
|
43
|
+
table test
|
44
|
+
table_option CREATE INDEX time_index ON testb (time);
|
45
|
+
</match>
|
46
|
+
|
47
|
+
=== Options
|
48
|
+
|
49
|
+
- Required
|
50
|
+
- database
|
51
|
+
- database name
|
52
|
+
- Optional
|
53
|
+
- table
|
54
|
+
- tablename. If not set, use +fluentd_store+. If not exists, creates automatically.
|
55
|
+
- host
|
56
|
+
- port
|
57
|
+
- user
|
58
|
+
- password
|
59
|
+
- table_option
|
60
|
+
- Add some SQL. This SQL is called only once when table is created from this plugin.
|
61
|
+
|
62
|
+
== Limitation
|
63
|
+
|
64
|
+
- Nested output is not allowd.
|
65
|
+
- Since using only one connection, performance may become bad. When you meet this, use connection pooling and write patch!
|
66
|
+
|
67
|
+
|
68
|
+
== Thanks
|
69
|
+
|
70
|
+
This source code is mainly borrowed from
|
71
|
+
{fluent-plugin-datacounter}{https://rubygems.org/gems/fluent-plugin-datacounter}. Thank you for tagomoris.
|
72
|
+
|
73
|
+
== Copyright
|
74
|
+
|
75
|
+
Copyright:: Copyright (c) 2012- Shirou WAKAYAMA
|
76
|
+
License:: Apache License
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
|
3
|
+
require 'rake/testtask'
|
4
|
+
Rake::TestTask.new(:test) do |test|
|
5
|
+
test.libs << 'lib' << 'test'
|
6
|
+
# test.pattern = 'test/**/test_*.rb'
|
7
|
+
test.test_files = FileList['test/**/test*.rb']
|
8
|
+
test.verbose = true
|
9
|
+
end
|
10
|
+
|
11
|
+
task :default => :test
|
12
|
+
|
data/example.conf
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
<source>
|
2
|
+
type tail
|
3
|
+
path /var/log/apache/access_log
|
4
|
+
tag apache.access
|
5
|
+
format apache
|
6
|
+
</source>
|
7
|
+
|
8
|
+
<match apache.*>
|
9
|
+
type pghstore
|
10
|
+
database test
|
11
|
+
table test # (option) default is fluentd_store
|
12
|
+
table_option CREATE INDEX time_index ON testb (time); # (option)
|
13
|
+
</match>
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "fluent-plugin-pghstore"
|
6
|
+
s.version = "0.0.1"
|
7
|
+
s.authors = ["WAKAYAMA Shirou"]
|
8
|
+
s.email = ["shirou.faw@gmail.com"]
|
9
|
+
s.homepage = "https://github.com/r_rudi/fluent-plugin-pghstore"
|
10
|
+
s.summary = %q{Output to PostgreSQL database which has a hstore extension}
|
11
|
+
s.description = %q{Output to PostgreSQL database which has a hstore extension}
|
12
|
+
|
13
|
+
s.rubyforge_project = "fluent-plugin-pghstore"
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
|
20
|
+
# specify any dependencies here; for example:
|
21
|
+
s.add_development_dependency "rspec"
|
22
|
+
# s.add_runtime_dependency "rest-client"
|
23
|
+
s.add_development_dependency "fluentd"
|
24
|
+
s.add_development_dependency "pg"
|
25
|
+
s.add_runtime_dependency "fluentd"
|
26
|
+
s.add_runtime_dependency "pg"
|
27
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
class Fluent::PgHStoreOutput < Fluent::BufferedOutput
|
2
|
+
Fluent::Plugin.register_output('pghstore', self)
|
3
|
+
|
4
|
+
config_param :database, :string
|
5
|
+
config_param :table, :string, :default => 'fluentd_store'
|
6
|
+
config_param :host, :string, :default => 'localhost'
|
7
|
+
config_param :port, :integer, :default => 5432
|
8
|
+
config_param :user, :string, :default => nil
|
9
|
+
config_param :password, :string, :default => nil
|
10
|
+
|
11
|
+
config_param :table_option, :string, :default => nil
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
super
|
15
|
+
require 'pg'
|
16
|
+
end
|
17
|
+
|
18
|
+
def start
|
19
|
+
super
|
20
|
+
|
21
|
+
@conn = get_connection(@database, @host, @port, @user, @password)
|
22
|
+
|
23
|
+
create_table(@table) unless table_exists?(@table)
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
def shutdown
|
28
|
+
super
|
29
|
+
|
30
|
+
@conn.close
|
31
|
+
end
|
32
|
+
|
33
|
+
def format(tag, time, record)
|
34
|
+
[tag, time, record].to_msgpack
|
35
|
+
end
|
36
|
+
|
37
|
+
def write(chunk)
|
38
|
+
chunk.msgpack_each {|(tag, time_str, record)|
|
39
|
+
sql = generate_sql(tag, time_str, record)
|
40
|
+
@conn.exec(sql)
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def generate_sql(tag, time, record)
|
47
|
+
kv_list = []
|
48
|
+
record.each {|(key,value)|
|
49
|
+
begin
|
50
|
+
v = Integer(value)
|
51
|
+
rescue ArgumentError => e
|
52
|
+
kv_list.push("#{key} => \"#{value}\"") # might be string
|
53
|
+
else
|
54
|
+
kv_list.push("#{key} => #{value}")
|
55
|
+
end
|
56
|
+
}
|
57
|
+
|
58
|
+
sql =<<"SQL"
|
59
|
+
INSERT INTO #{@table} (tag, time, record) VALUES
|
60
|
+
('#{tag}', '#{Time.at(time)}'::TIMESTAMP WITH TIME ZONE, '#{kv_list.join(",")}');
|
61
|
+
SQL
|
62
|
+
return sql
|
63
|
+
end
|
64
|
+
|
65
|
+
def get_connection(dbname, host, port, user, password)
|
66
|
+
if user
|
67
|
+
return PG.connect(:dbname => dbname, :host => host, :port => port,
|
68
|
+
:user => user, :password => password)
|
69
|
+
else
|
70
|
+
return PG.connect(:dbname => dbname, :host => host, :port => port)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def table_exists?(table)
|
75
|
+
sql =<<"SQL"
|
76
|
+
SELECT COUNT(*) FROM pg_tables WHERE tablename = '#{table}';
|
77
|
+
SQL
|
78
|
+
res = @conn.exec(sql)
|
79
|
+
if res[0]["count"] == "1"
|
80
|
+
return true
|
81
|
+
else
|
82
|
+
return false
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def create_table(tablename)
|
87
|
+
sql =<<"SQL"
|
88
|
+
CREATE TABLE #{tablename} (
|
89
|
+
tag TEXT,
|
90
|
+
time TIMESTAMP WITH TIME ZONE,
|
91
|
+
record HSTORE
|
92
|
+
);
|
93
|
+
SQL
|
94
|
+
|
95
|
+
sql += @table_option if @table_option
|
96
|
+
|
97
|
+
@conn.exec(sql)
|
98
|
+
|
99
|
+
$log.warn "#{tablename} table is not exists. created."
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,28 @@
|
|
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
|
10
|
+
require 'test/unit'
|
11
|
+
|
12
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
13
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
14
|
+
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
|
+
|
25
|
+
require 'fluent/plugin/out_pghstore'
|
26
|
+
|
27
|
+
class Test::Unit::TestCase
|
28
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class PGHStoreOutputTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
Fluent::Test.setup
|
6
|
+
end
|
7
|
+
|
8
|
+
CONFIG = %[
|
9
|
+
database "testdb"
|
10
|
+
table "testtable"
|
11
|
+
user "testuser"
|
12
|
+
password "testpassword"
|
13
|
+
]
|
14
|
+
|
15
|
+
def create_driver(conf = CONFIG, tag='test.input')
|
16
|
+
Fluent::Test::BufferedOutputTestDriver.new(Fluent::PgHStoreOutput, tag).configure(conf)
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_configure
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_format
|
23
|
+
d = create_driver
|
24
|
+
|
25
|
+
# time = Time.parse("2011-01-02 13:14:15 UTC").to_i
|
26
|
+
# d.emit({"a"=>1}, time)
|
27
|
+
# d.emit({"a"=>2}, time)
|
28
|
+
|
29
|
+
# d.expect_format %[2011-01-02T13:14:15Z\ttest\t{"a":1}\n]
|
30
|
+
# d.expect_format %[2011-01-02T13:14:15Z\ttest\t{"a":2}\n]
|
31
|
+
|
32
|
+
# d.run
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_write
|
36
|
+
d = create_driver
|
37
|
+
|
38
|
+
# time = Time.parse("2011-01-02 13:14:15 UTC").to_i
|
39
|
+
# d.emit({"a"=>1}, time)
|
40
|
+
# d.emit({"a"=>2}, time)
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,277 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class DataCounterOutputTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
Fluent::Test.setup
|
6
|
+
end
|
7
|
+
|
8
|
+
CONFIG = %[
|
9
|
+
unit minute
|
10
|
+
aggregate tag
|
11
|
+
input_tag_remove_prefix test
|
12
|
+
count_key target
|
13
|
+
pattern1 status2xx ^2\\d\\d$
|
14
|
+
pattern2 status3xx ^3\\d\\d$
|
15
|
+
pattern3 status4xx ^4\\d\\d$
|
16
|
+
pattern4 status5xx ^5\\d\\d$
|
17
|
+
]
|
18
|
+
|
19
|
+
def create_driver(conf = CONFIG, tag='test.input')
|
20
|
+
Fluent::Test::OutputTestDriver.new(Fluent::DataCounterOutput, tag).configure(conf)
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_configure
|
24
|
+
assert_raise(Fluent::ConfigError) {
|
25
|
+
d = create_driver('')
|
26
|
+
}
|
27
|
+
assert_raise(Fluent::ConfigError) {
|
28
|
+
d = create_driver %[
|
29
|
+
count_key field
|
30
|
+
]
|
31
|
+
}
|
32
|
+
assert_raise(Fluent::ConfigError) {
|
33
|
+
d = create_driver %[
|
34
|
+
pattern1 hoge ^1\\d\\d$
|
35
|
+
]
|
36
|
+
}
|
37
|
+
assert_raise(Fluent::ConfigError) {
|
38
|
+
d = create_driver %[
|
39
|
+
count_key field
|
40
|
+
pattern2 hoge ^1\\d\\d$
|
41
|
+
]
|
42
|
+
}
|
43
|
+
assert_raise(Fluent::ConfigError) {
|
44
|
+
d = create_driver %[
|
45
|
+
count_key field
|
46
|
+
pattern1 hoge ^1\\d\\d$
|
47
|
+
pattern4 pos ^4\\d\\d$
|
48
|
+
]
|
49
|
+
}
|
50
|
+
assert_raise(Fluent::ConfigError) {
|
51
|
+
d = create_driver %[
|
52
|
+
count_key field
|
53
|
+
pattern1 hoge ^1\\d\\d$
|
54
|
+
pattern2 hoge ^4\\d\\d$
|
55
|
+
]
|
56
|
+
}
|
57
|
+
d = create_driver %[
|
58
|
+
count_key field
|
59
|
+
pattern1 ok ^2\\d\\d$
|
60
|
+
]
|
61
|
+
assert_equal 60, d.instance.tick
|
62
|
+
assert_equal :tag, d.instance.aggregate
|
63
|
+
assert_equal 'datacount', d.instance.tag
|
64
|
+
assert_nil d.instance.input_tag_remove_prefix
|
65
|
+
assert_equal 'field', d.instance.count_key
|
66
|
+
assert_equal 'ok ^2\d\d$', d.instance.pattern1
|
67
|
+
|
68
|
+
d1 = create_driver %[
|
69
|
+
unit minute
|
70
|
+
count_key field
|
71
|
+
pattern1 ok ^2\\d\\d$
|
72
|
+
]
|
73
|
+
d2 = create_driver %[
|
74
|
+
count_interval 60s
|
75
|
+
count_key field
|
76
|
+
pattern1 ok ^2\\d\\d$
|
77
|
+
]
|
78
|
+
assert_equal d1.instance.tick, d2.instance.tick
|
79
|
+
|
80
|
+
d = create_driver %[
|
81
|
+
count_interval 5m
|
82
|
+
count_key field
|
83
|
+
pattern1 ok ^2\\d\\d$
|
84
|
+
]
|
85
|
+
assert_equal 300, d.instance.tick
|
86
|
+
|
87
|
+
d = create_driver %[
|
88
|
+
count_interval 2h
|
89
|
+
count_key field
|
90
|
+
pattern1 ok ^2\\d\\d$
|
91
|
+
]
|
92
|
+
assert_equal 7200, d.instance.tick
|
93
|
+
|
94
|
+
d = create_driver %[
|
95
|
+
count_interval 30s
|
96
|
+
count_key field
|
97
|
+
pattern1 ok ^2\\d\\d$
|
98
|
+
]
|
99
|
+
assert_equal 30, d.instance.tick
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_count_initialized
|
103
|
+
d = create_driver %[
|
104
|
+
aggregate all
|
105
|
+
count_key field
|
106
|
+
pattern1 hoge 1\d\d
|
107
|
+
pattern2 moge 2\d\d
|
108
|
+
]
|
109
|
+
assert_equal [0,0,0], d.instance.counts['all']
|
110
|
+
end
|
111
|
+
|
112
|
+
def test_countups
|
113
|
+
d = create_driver
|
114
|
+
assert_nil d.instance.counts['test.input']
|
115
|
+
|
116
|
+
d.instance.countups('test.input', [0, 0, 0, 0, 0])
|
117
|
+
assert_equal [0,0,0,0,0], d.instance.counts['test.input']
|
118
|
+
d.instance.countups('test.input', [1, 1, 1, 0, 0])
|
119
|
+
assert_equal [1,1,1,0,0], d.instance.counts['test.input']
|
120
|
+
d.instance.countups('test.input', [0, 5, 1, 0, 0])
|
121
|
+
assert_equal [1,6,2,0,0], d.instance.counts['test.input']
|
122
|
+
end
|
123
|
+
|
124
|
+
def test_stripped_tag
|
125
|
+
d = create_driver
|
126
|
+
assert_equal 'input', d.instance.stripped_tag('test.input')
|
127
|
+
assert_equal 'test.input', d.instance.stripped_tag('test.test.input')
|
128
|
+
assert_equal 'input', d.instance.stripped_tag('input')
|
129
|
+
end
|
130
|
+
|
131
|
+
def test_generate_output
|
132
|
+
d = create_driver
|
133
|
+
r1 = d.instance.generate_output({'test.input' => [60,240,120,180,0], 'test.input2' => [0,600,0,0,0]}, 60)
|
134
|
+
assert_equal 60, r1['input_unmatched_count']
|
135
|
+
assert_equal 1.0, r1['input_unmatched_rate']
|
136
|
+
assert_equal 10.0, r1['input_unmatched_percentage']
|
137
|
+
assert_equal 240, r1['input_status2xx_count']
|
138
|
+
assert_equal 4.0, r1['input_status2xx_rate']
|
139
|
+
assert_equal 40.0, r1['input_status2xx_percentage']
|
140
|
+
assert_equal 120, r1['input_status3xx_count']
|
141
|
+
assert_equal 2.0, r1['input_status3xx_rate']
|
142
|
+
assert_equal 20.0, r1['input_status3xx_percentage']
|
143
|
+
assert_equal 180, r1['input_status4xx_count']
|
144
|
+
assert_equal 3.0, r1['input_status4xx_rate']
|
145
|
+
assert_equal 30.0, r1['input_status4xx_percentage']
|
146
|
+
assert_equal 0, r1['input_status5xx_count']
|
147
|
+
assert_equal 0.0, r1['input_status5xx_rate']
|
148
|
+
assert_equal 0.0, r1['input_status5xx_percentage']
|
149
|
+
|
150
|
+
assert_equal 0, r1['input2_unmatched_count']
|
151
|
+
assert_equal 0.0, r1['input2_unmatched_rate']
|
152
|
+
assert_equal 0.0, r1['input2_unmatched_percentage']
|
153
|
+
assert_equal 600, r1['input2_status2xx_count']
|
154
|
+
assert_equal 10.0, r1['input2_status2xx_rate']
|
155
|
+
assert_equal 100.0, r1['input2_status2xx_percentage']
|
156
|
+
assert_equal 0, r1['input2_status3xx_count']
|
157
|
+
assert_equal 0.0, r1['input2_status3xx_rate']
|
158
|
+
assert_equal 0.0, r1['input2_status3xx_percentage']
|
159
|
+
assert_equal 0, r1['input2_status4xx_count']
|
160
|
+
assert_equal 0.0, r1['input2_status4xx_rate']
|
161
|
+
assert_equal 0.0, r1['input2_status4xx_percentage']
|
162
|
+
assert_equal 0, r1['input2_status5xx_count']
|
163
|
+
assert_equal 0.0, r1['input2_status5xx_rate']
|
164
|
+
assert_equal 0.0, r1['input2_status5xx_percentage']
|
165
|
+
|
166
|
+
d = create_driver %[
|
167
|
+
aggregate all
|
168
|
+
count_key field
|
169
|
+
pattern1 hoge xxx\d\d
|
170
|
+
]
|
171
|
+
r2 = d.instance.generate_output({'all' => [60,240]}, 60)
|
172
|
+
assert_equal 60, r2['unmatched_count']
|
173
|
+
assert_equal 1.0, r2['unmatched_rate']
|
174
|
+
assert_equal 20.0, r2['unmatched_percentage']
|
175
|
+
assert_equal 240, r2['hoge_count']
|
176
|
+
assert_equal 4.0, r2['hoge_rate']
|
177
|
+
assert_equal 80.0, r2['hoge_percentage']
|
178
|
+
end
|
179
|
+
|
180
|
+
def test_pattern_num
|
181
|
+
assert_equal 20, Fluent::DataCounterOutput::PATTERN_MAX_NUM
|
182
|
+
|
183
|
+
conf = %[
|
184
|
+
aggregate all
|
185
|
+
count_key field
|
186
|
+
]
|
187
|
+
(1..20).each do |i|
|
188
|
+
conf += "pattern#{i} name#{i} ^#{i}$\n"
|
189
|
+
end
|
190
|
+
d = create_driver(conf, 'test.max')
|
191
|
+
d.run do
|
192
|
+
(1..20).each do |j|
|
193
|
+
d.emit({'field' => j})
|
194
|
+
end
|
195
|
+
end
|
196
|
+
r = d.instance.flush(60)
|
197
|
+
assert_equal 1, r['name1_count']
|
198
|
+
assert_equal 1, r['name2_count']
|
199
|
+
assert_equal 1, r['name3_count']
|
200
|
+
assert_equal 1, r['name4_count']
|
201
|
+
assert_equal 1, r['name5_count']
|
202
|
+
assert_equal 1, r['name6_count']
|
203
|
+
assert_equal 1, r['name7_count']
|
204
|
+
assert_equal 1, r['name8_count']
|
205
|
+
assert_equal 1, r['name9_count']
|
206
|
+
assert_equal 1, r['name10_count']
|
207
|
+
assert_equal 1, r['name11_count']
|
208
|
+
assert_equal 1, r['name12_count']
|
209
|
+
assert_equal 1, r['name13_count']
|
210
|
+
assert_equal 1, r['name14_count']
|
211
|
+
assert_equal 1, r['name15_count']
|
212
|
+
assert_equal 1, r['name16_count']
|
213
|
+
assert_equal 1, r['name17_count']
|
214
|
+
assert_equal 1, r['name18_count']
|
215
|
+
assert_equal 1, r['name19_count']
|
216
|
+
assert_equal 1, r['name20_count']
|
217
|
+
end
|
218
|
+
|
219
|
+
def test_emit
|
220
|
+
d1 = create_driver(CONFIG, 'test.tag1')
|
221
|
+
d1.run do
|
222
|
+
60.times do
|
223
|
+
d1.emit({'target' => '200'})
|
224
|
+
d1.emit({'target' => '100'})
|
225
|
+
d1.emit({'target' => '200'})
|
226
|
+
d1.emit({'target' => '400'})
|
227
|
+
end
|
228
|
+
end
|
229
|
+
r1 = d1.instance.flush(60)
|
230
|
+
assert_equal 120, r1['tag1_status2xx_count']
|
231
|
+
assert_equal 2.0, r1['tag1_status2xx_rate']
|
232
|
+
assert_equal 50.0, r1['tag1_status2xx_percentage']
|
233
|
+
|
234
|
+
assert_equal 60, r1['tag1_status4xx_count']
|
235
|
+
assert_equal 1.0, r1['tag1_status4xx_rate']
|
236
|
+
assert_equal 25.0, r1['tag1_status4xx_percentage']
|
237
|
+
|
238
|
+
assert_equal 60, r1['tag1_unmatched_count']
|
239
|
+
assert_equal 1.0, r1['tag1_unmatched_rate']
|
240
|
+
assert_equal 25.0, r1['tag1_unmatched_percentage']
|
241
|
+
|
242
|
+
assert_equal 0, r1['tag1_status3xx_count']
|
243
|
+
assert_equal 0.0, r1['tag1_status3xx_rate']
|
244
|
+
assert_equal 0.0, r1['tag1_status3xx_percentage']
|
245
|
+
assert_equal 0, r1['tag1_status5xx_count']
|
246
|
+
assert_equal 0.0, r1['tag1_status5xx_rate']
|
247
|
+
assert_equal 0.0, r1['tag1_status5xx_percentage']
|
248
|
+
|
249
|
+
d2 = create_driver(%[
|
250
|
+
aggregate all
|
251
|
+
count_key target
|
252
|
+
pattern1 ok 2\\d\\d
|
253
|
+
pattern2 redirect 3\\d\\d
|
254
|
+
], 'test.tag2')
|
255
|
+
d2.run do
|
256
|
+
60.times do
|
257
|
+
d2.emit({'target' => '200'})
|
258
|
+
d2.emit({'target' => '300 200'})
|
259
|
+
end
|
260
|
+
end
|
261
|
+
d2.instance.flush_emit(120)
|
262
|
+
emits = d2.emits
|
263
|
+
assert_equal 1, emits.length
|
264
|
+
data = emits[0]
|
265
|
+
assert_equal 'datacount', data[0] # tag
|
266
|
+
assert_equal 120, data[2]['ok_count']
|
267
|
+
assert_equal 1.0, data[2]['ok_rate']
|
268
|
+
assert_equal 100.0, data[2]['ok_percentage']
|
269
|
+
assert_equal 0, data[2]['redirect_count']
|
270
|
+
assert_equal 0.0, data[2]['redirect_rate']
|
271
|
+
assert_equal 0.0, data[2]['redirect_percentage']
|
272
|
+
assert_equal 0, data[2]['unmatched_count']
|
273
|
+
assert_equal 0.0, data[2]['unmatched_rate']
|
274
|
+
assert_equal 0.0, data[2]['unmatched_percentage']
|
275
|
+
end
|
276
|
+
|
277
|
+
end
|
metadata
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fluent-plugin-pghstore
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- WAKAYAMA Shirou
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-03-30 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: fluentd
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: pg
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: fluentd
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: pg
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :runtime
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
description: Output to PostgreSQL database which has a hstore extension
|
95
|
+
email:
|
96
|
+
- shirou.faw@gmail.com
|
97
|
+
executables: []
|
98
|
+
extensions: []
|
99
|
+
extra_rdoc_files: []
|
100
|
+
files:
|
101
|
+
- .gitignore
|
102
|
+
- Gemfile
|
103
|
+
- LICENSE.txt
|
104
|
+
- README.rdoc
|
105
|
+
- Rakefile
|
106
|
+
- example.conf
|
107
|
+
- fluent-plugin-pghstore.gemspec
|
108
|
+
- lib/fluent/plugin/out_pghstore.rb
|
109
|
+
- test/helper.rb
|
110
|
+
- test/plugin/test_out_pghstore.rb
|
111
|
+
- test/plugin/test_out_pghstore.rb.bak
|
112
|
+
homepage: https://github.com/r_rudi/fluent-plugin-pghstore
|
113
|
+
licenses: []
|
114
|
+
post_install_message:
|
115
|
+
rdoc_options: []
|
116
|
+
require_paths:
|
117
|
+
- lib
|
118
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
119
|
+
none: false
|
120
|
+
requirements:
|
121
|
+
- - ! '>='
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '0'
|
124
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
125
|
+
none: false
|
126
|
+
requirements:
|
127
|
+
- - ! '>='
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
version: '0'
|
130
|
+
requirements: []
|
131
|
+
rubyforge_project: fluent-plugin-pghstore
|
132
|
+
rubygems_version: 1.8.21
|
133
|
+
signing_key:
|
134
|
+
specification_version: 3
|
135
|
+
summary: Output to PostgreSQL database which has a hstore extension
|
136
|
+
test_files: []
|