fluent-plugin-postgresql-csvlog 0.10.0 → 0.11.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 +4 -4
- data/fluent-plugin-postgresql-csvlog.gemspec +1 -1
- data/lib/fluent/plugin/filter_postgresql_redactor.rb +12 -26
- data/lib/fluent/plugin/in_pg_stat_activity.rb +12 -24
- data/lib/fluent/plugin/in_pg_stat_statements.rb +14 -17
- data/lib/fluent/plugin/query_normalizer.rb +42 -0
- data/test/plugin/test_filter_postgresql_redactor.rb +4 -4
- data/test/plugin/test_in_pg_stat_activity.rb +1 -2
- data/test/plugin/test_in_pg_stat_statements.rb +2 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: af7b81970b58d90863db57c0386b096ca5fe62f4b5c100cd62a6a40b416c15a1
|
4
|
+
data.tar.gz: 53fdf00e4405b46f5db1a40abc29a1d8e1ffe2daeb9f3597027f96fb9fb2d1c0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f69402f67122706714e07283efc6b131f8c8b4f06851ed77b01d5188d305dd9d57e5e62a3d732cba0272b06db6c5e266d953fb19edb5af27d4396e715ca05be7
|
7
|
+
data.tar.gz: ae1f30e585ace5fdba2d81921f22eec5f2a99a0ae3cec8ae71e2e19fa27b89e10f4f81e7057edbea913582920dc3d4bdcb81195a42e13c21077bccd1667da368
|
@@ -2,7 +2,7 @@ $:.push File.expand_path('lib', __dir__)
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = 'fluent-plugin-postgresql-csvlog'
|
5
|
-
s.version = '0.
|
5
|
+
s.version = '0.11.0'
|
6
6
|
s.authors = ['stanhu']
|
7
7
|
s.email = ['stanhu@gmail.com']
|
8
8
|
s.homepage = 'https://gitlab.com/gitlab-org/fluent-plugins/fluent-plugin-postgresql-csvlog'
|
@@ -1,7 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'fluent/plugin/filter'
|
4
|
-
|
4
|
+
|
5
|
+
require_relative './query_normalizer'
|
5
6
|
|
6
7
|
module Fluent::Plugin
|
7
8
|
class PostgreSQLRedactor < Filter
|
@@ -19,32 +20,17 @@ module Fluent::Plugin
|
|
19
20
|
desc 'Truncate the query if it exceeds the number of bytes'
|
20
21
|
config_param :max_length, :integer, default: 3 * 1024 * 1024
|
21
22
|
|
22
|
-
|
23
|
-
statement = record[@input_key]
|
24
|
-
|
25
|
-
return record unless statement
|
26
|
-
|
27
|
-
normalized = PgQuery.normalize(statement)
|
28
|
-
record[@fingerprint_key] = PgQuery.fingerprint(normalized) if @fingerprint_key
|
29
|
-
|
30
|
-
record.delete(@input_key)
|
31
|
-
record[@output_key] = truncate_query(record, normalized)
|
23
|
+
include QueryNormalizer
|
32
24
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
return normalized if normalized.length < @max_length
|
43
|
-
|
44
|
-
record['truncated_query'] = true
|
45
|
-
# Assume UTF-8 encoding for PostgreSQL queries
|
46
|
-
utf8_query = normalized.encode('UTF-8', invalid: :replace, undef: :replace, replace: '')
|
47
|
-
utf8_query[0, @max_length]
|
25
|
+
def filter(_tag, _time, record)
|
26
|
+
opts = {
|
27
|
+
input_key: @input_key,
|
28
|
+
output_key: @output_key,
|
29
|
+
fingerprint_key: @fingerprint_key,
|
30
|
+
max_length: @max_length
|
31
|
+
}
|
32
|
+
|
33
|
+
normalize_and_fingerprint_query(record, opts)
|
48
34
|
end
|
49
35
|
end
|
50
36
|
end
|
@@ -12,6 +12,8 @@ module Fluent::Plugin
|
|
12
12
|
# Fingerprints of the queries are also included for easier aggregation
|
13
13
|
class PgStatActivityInput < PollingPostgresInputPlugin
|
14
14
|
include MarginaliaExtractor
|
15
|
+
include QueryNormalizer
|
16
|
+
|
15
17
|
Fluent::Plugin.register_input('pg_stat_activity', self)
|
16
18
|
|
17
19
|
ACTIVITY_QUERY = <<-SQL
|
@@ -42,6 +44,9 @@ module Fluent::Plugin
|
|
42
44
|
desc 'Name of field to store SQL query fingerprint'
|
43
45
|
config_param :fingerprint_key, :string, default: 'fingerprint'
|
44
46
|
|
47
|
+
desc 'Truncate the query if it exceeds the number of bytes'
|
48
|
+
config_param :max_length, :integer, default: 3 * 1024 * 1024
|
49
|
+
|
45
50
|
protected
|
46
51
|
|
47
52
|
def on_poll
|
@@ -94,31 +99,14 @@ module Fluent::Plugin
|
|
94
99
|
# Inject marginalia into record
|
95
100
|
parse_marginalia_into_record(record, 'query', true)
|
96
101
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
end
|
104
|
-
|
105
|
-
def fingerprint_query(query)
|
106
|
-
# We record the query_length as it will help in understanding whether unparseable
|
107
|
-
# queries are truncated.
|
108
|
-
record = { 'query_length' => query&.length, 'query' => nil }
|
109
|
-
|
110
|
-
return record unless query
|
111
|
-
|
112
|
-
normalized = PgQuery.normalize(query)
|
113
|
-
record['query'] = normalized
|
114
|
-
|
115
|
-
record[@fingerprint_key] = PgQuery.fingerprint(normalized) if @fingerprint_key
|
116
|
-
|
117
|
-
record
|
118
|
-
rescue PgQuery::ParseError
|
119
|
-
record['query_unparseable'] = true
|
102
|
+
opts = {
|
103
|
+
input_key: 'query',
|
104
|
+
output_key: 'query',
|
105
|
+
fingerprint_key: @fingerprint_key,
|
106
|
+
max_length: @max_length
|
107
|
+
}
|
120
108
|
|
121
|
-
record
|
109
|
+
normalize_and_fingerprint_query(record, opts)
|
122
110
|
end
|
123
111
|
end
|
124
112
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative './polling_pg_input_plugin'
|
4
|
-
|
4
|
+
require_relative './query_normalizer'
|
5
5
|
|
6
6
|
module Fluent::Plugin
|
7
7
|
# PgStatStatementsInput will periodically poll postgres, querying pg_stat_statements
|
@@ -19,6 +19,11 @@ module Fluent::Plugin
|
|
19
19
|
desc 'Name of field to store SQL query fingerprint'
|
20
20
|
config_param :fingerprint_key, :string, default: 'fingerprint'
|
21
21
|
|
22
|
+
desc 'Truncate the query if it exceeds the number of bytes'
|
23
|
+
config_param :max_length, :integer, default: 3 * 1024 * 1024
|
24
|
+
|
25
|
+
include QueryNormalizer
|
26
|
+
|
22
27
|
POSTGRES_SERVER_VERSION_QUERY = "SELECT current_setting('server_version_num')"
|
23
28
|
|
24
29
|
PG12_STAT_STATEMENTS_QUERY = <<-SQL
|
@@ -56,30 +61,22 @@ module Fluent::Plugin
|
|
56
61
|
|
57
62
|
# Returns a fluentd record for a query row
|
58
63
|
def record_for_row(row)
|
59
|
-
query = row['query']
|
60
|
-
|
61
|
-
# We record the query_length as it will help in understanding whether unparseable
|
62
|
-
# queries are truncated.
|
63
64
|
record = {
|
65
|
+
'query' => row['query'],
|
64
66
|
'queryid' => row['queryid'].to_s,
|
65
|
-
'query_length' => query&.length,
|
66
67
|
'calls' => row['calls']&.to_i,
|
67
68
|
'total_time_ms' => row['total_time']&.to_f,
|
68
69
|
'rows' => row['rows']&.to_i
|
69
70
|
}
|
70
71
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
record
|
79
|
-
rescue PgQuery::ParseError
|
80
|
-
record['query_unparseable'] = true
|
72
|
+
opts = {
|
73
|
+
input_key: 'query',
|
74
|
+
output_key: 'query',
|
75
|
+
fingerprint_key: @fingerprint_key,
|
76
|
+
max_length: @max_length
|
77
|
+
}
|
81
78
|
|
82
|
-
record
|
79
|
+
normalize_and_fingerprint_query(record, opts)
|
83
80
|
end
|
84
81
|
|
85
82
|
# Query the database and emit statements to fluentd router
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'pg_query'
|
4
|
+
|
5
|
+
module Fluent::Plugin
|
6
|
+
module QueryNormalizer
|
7
|
+
def normalize_and_fingerprint_query(record, opts)
|
8
|
+
input_key = opts[:input_key]
|
9
|
+
output_key = opts[:output_key]
|
10
|
+
fingerprint_key = opts[:fingerprint_key]
|
11
|
+
max_length = opts[:max_length]
|
12
|
+
statement = record[input_key]
|
13
|
+
|
14
|
+
return record unless statement
|
15
|
+
|
16
|
+
# We record the query_length as it will help in understanding whether unparseable
|
17
|
+
# queries are truncated.
|
18
|
+
record['query_length'] = statement&.length
|
19
|
+
normalized = PgQuery.normalize(statement)
|
20
|
+
|
21
|
+
record.delete(input_key)
|
22
|
+
record[fingerprint_key] = PgQuery.fingerprint(normalized) if fingerprint_key
|
23
|
+
record[output_key] = truncate_query(record, normalized, max_length)
|
24
|
+
|
25
|
+
record
|
26
|
+
rescue PgQuery::ParseError
|
27
|
+
record['query_unparseable'] = true
|
28
|
+
record
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def truncate_query(record, normalized, max_length)
|
34
|
+
return normalized if normalized.length < max_length
|
35
|
+
|
36
|
+
record['truncated_query'] = true
|
37
|
+
# Assume UTF-8 encoding for PostgreSQL queries
|
38
|
+
utf8_query = normalized.encode('UTF-8', invalid: :replace, undef: :replace, replace: '')
|
39
|
+
utf8_query[0, max_length]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -27,7 +27,7 @@ class PostgreSQLRedactorTest < Test::Unit::TestCase
|
|
27
27
|
inputs.each { |input| d.feed(input) }
|
28
28
|
end
|
29
29
|
|
30
|
-
assert_equal(%w[duration_s fingerprint message sql], d.filtered[0].last.keys.sort)
|
30
|
+
assert_equal(%w[duration_s fingerprint message query_length sql], d.filtered[0].last.keys.sort)
|
31
31
|
assert_equal('SELECT * FROM projects WHERE id = $1', d.filtered[0].last['sql'])
|
32
32
|
end
|
33
33
|
|
@@ -47,7 +47,7 @@ class PostgreSQLRedactorTest < Test::Unit::TestCase
|
|
47
47
|
inputs.each { |input| d.feed(input) }
|
48
48
|
end
|
49
49
|
|
50
|
-
assert_equal(%w[duration_s fingerprint message sql truncated_query], d.filtered[0].last.keys.sort)
|
50
|
+
assert_equal(%w[duration_s fingerprint message query_length sql truncated_query], d.filtered[0].last.keys.sort)
|
51
51
|
assert_equal('SELECT * F', d.filtered[0].last['sql'])
|
52
52
|
end
|
53
53
|
|
@@ -60,7 +60,7 @@ class PostgreSQLRedactorTest < Test::Unit::TestCase
|
|
60
60
|
d.feed(input)
|
61
61
|
end
|
62
62
|
|
63
|
-
assert_equal(%w[
|
63
|
+
assert_equal(%w[query query_length query_unparseable], d.filtered[0].last.keys.sort)
|
64
64
|
assert_equal(input['query'], d.filtered[0].last['query'])
|
65
65
|
end
|
66
66
|
|
@@ -83,7 +83,7 @@ class PostgreSQLRedactorTest < Test::Unit::TestCase
|
|
83
83
|
inputs.each { |input| d.feed(input) }
|
84
84
|
end
|
85
85
|
|
86
|
-
assert_equal(%w[duration_s fingerprint message out_sql], d.filtered[0].last.keys.sort)
|
86
|
+
assert_equal(%w[duration_s fingerprint message out_sql query_length], d.filtered[0].last.keys.sort)
|
87
87
|
assert_equal('SELECT * FROM projects WHERE id = $1', d.filtered[0].last['out_sql'])
|
88
88
|
end
|
89
89
|
end
|
@@ -109,7 +109,6 @@ class PgStatActivityInputTest < Test::Unit::TestCase
|
|
109
109
|
'pid' => nil,
|
110
110
|
'query' => nil,
|
111
111
|
'query_age_s' => nil,
|
112
|
-
'query_length' => nil,
|
113
112
|
'query_start' => nil,
|
114
113
|
'state' => nil,
|
115
114
|
'state_age_s' => nil,
|
@@ -159,7 +158,7 @@ class PgStatActivityInputTest < Test::Unit::TestCase
|
|
159
158
|
'datid' => 16384,
|
160
159
|
'datname' => 'testuser',
|
161
160
|
'pid' => 376,
|
162
|
-
'query' =>
|
161
|
+
'query' => "SELECT * FROM users WHERE user_se=",
|
163
162
|
'query_age_s' => 0.001894,
|
164
163
|
'query_length' => 34,
|
165
164
|
'query_start' => '2021-07-23T12:55:25.000+00:00',
|
@@ -77,8 +77,8 @@ class PgStatStatementsInputTest < Test::Unit::TestCase
|
|
77
77
|
record = d.instance.record_for_row(row)
|
78
78
|
|
79
79
|
expected = {
|
80
|
-
'query_length' => nil,
|
81
80
|
'queryid' => '1234',
|
81
|
+
'query' => nil,
|
82
82
|
'calls' => nil,
|
83
83
|
'rows' => nil,
|
84
84
|
'total_time_ms' => nil
|
@@ -153,6 +153,7 @@ class PgStatStatementsInputTest < Test::Unit::TestCase
|
|
153
153
|
expected = {
|
154
154
|
'query_length' => 13,
|
155
155
|
'query_unparseable' => true,
|
156
|
+
'query' => 'SELECT * FROM',
|
156
157
|
'queryid' => '1234',
|
157
158
|
'calls' => nil,
|
158
159
|
'rows' => nil,
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fluent-plugin-postgresql-csvlog
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- stanhu
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-10-
|
11
|
+
date: 2024-10-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: fluentd
|
@@ -111,6 +111,7 @@ files:
|
|
111
111
|
- lib/fluent/plugin/marginalia_extractor.rb
|
112
112
|
- lib/fluent/plugin/parser_multiline_csv.rb
|
113
113
|
- lib/fluent/plugin/polling_pg_input_plugin.rb
|
114
|
+
- lib/fluent/plugin/query_normalizer.rb
|
114
115
|
- sql/create_extension.sql
|
115
116
|
- test/helper.rb
|
116
117
|
- test/plugin/itest_in_pg_stat_activity.rb
|