belong-plugin-rds-pgsql-log 0.3.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 267e5edc1911383cae44d361ef21b95de6d91ed3
4
+ data.tar.gz: 03ce46a488050250e61591e52913aa8ee1e3241b
5
+ SHA512:
6
+ metadata.gz: b75ff1e633584342f4786f79761310f5ea70bd0cd89ae814f36f9c55f0938a9cbfcfe8b4a4d24b257c5a5b1fbc1ea8db585d0d85f0d84cbf24438309659e997c
7
+ data.tar.gz: 9f05d98fe5b3b23d421896b79fdc262e525105307acf4d28efc0820d6eef8f9261a44551735f8c86157275c698751329307eeaa8e62f9c0caa177a8a07be6df4
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ *.gem
2
+ fluent.conf
3
+
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 shinsaka
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,80 @@
1
+ # Amazon RDS for PostgreSQL log input plugin for fluentd
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/fluent-plugin-rds-pgsql-log.svg)](https://badge.fury.io/rb/fluent-plugin-rds-pgsql-log)
4
+
5
+ ## Overview
6
+ - Amazon Web Services RDS log input plugin for fluentd
7
+
8
+ ## Installation
9
+
10
+ $ fluentd-gem install fluent-plugin-rds-pgsql-log
11
+
12
+ ## AWS ELB Settings
13
+ - settings see: [PostgreSQL Database Log Files](http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_LogAccess.Concepts.PostgreSQL.html)
14
+
15
+ ## When SSL certification error
16
+ log:
17
+ ```
18
+ SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed
19
+ ```
20
+ Do env setting follows:
21
+ ```
22
+ SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt (If you using amazon linux)
23
+ ```
24
+
25
+ ## Configuration
26
+
27
+ ```config
28
+ <source>
29
+ type rds_pgsql_log
30
+ # required
31
+ region <region name>
32
+ db_instance_identifier <instance identifier>
33
+ # optional if you can IAM credentials
34
+ access_key_id <access_key>
35
+ secret_access_key <secret_access_key>
36
+ # optional
37
+ refresh_interval <interval number by second(default: 30)>
38
+ tag <tag name(default: rds-pgsql.log>
39
+ pos_file <log getting position file(default: rds-pgsql.log)>
40
+ </source>
41
+ ```
42
+
43
+ ### Example setting
44
+ ```config
45
+ <source>
46
+ type rds_pgsql_log
47
+ region ap-northeast-1
48
+ db_instance_identifier test-postgres
49
+ access_key_id XXXXXXXXXXXXXXXXXXXX
50
+ secret_access_key xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
51
+ refresh_interval 30
52
+ tag pgsql.log
53
+ pos_file /tmp/pgsql-log-pos.dat
54
+ </source>
55
+
56
+ <match pgsql.log>
57
+ type stdout
58
+ </match>
59
+ ```
60
+
61
+ ### json output example
62
+ ```
63
+
64
+ {"time":"2015-05-30 02:19:22 UTC",
65
+ "host":"192.168.30.175(53092)",
66
+ "user":"testuser",
67
+ "database":"db1",
68
+ "pid":"9769",
69
+ "message_level":"LOG",
70
+ "message":" statement: select 1;",
71
+ "log_file_name":"error/postgresql.log.2015-05-30-02"
72
+ }
73
+ ```
74
+
75
+ ## Changes
76
+ ### 0.2.0
77
+ - require fluentd version 0.14.0
78
+
79
+ ### 0.3.0
80
+ - use AWS SDK v3.x
@@ -0,0 +1,23 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+
4
+ Gem::Specification.new do |spec|
5
+ spec.name = "belong-plugin-rds-pgsql-log"
6
+ spec.version = "0.3.1"
7
+ spec.authors = ["shinsaka", "phani"]
8
+ spec.email = ["shinx1265@gmail.com", "phani@belong.co"]
9
+ spec.summary = "Amazon RDS for PostgreSQL log input plugin"
10
+ spec.description = "fluentd plugin for Amazon RDS for PostgreSQL log input with a fix for timestamp"
11
+ spec.homepage = "https://github.com/belongco/fluent-plugin-rds-pgsql-log"
12
+ spec.license = "MIT"
13
+
14
+ spec.files = `git ls-files -z`.split("\x0")
15
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
16
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
17
+ spec.require_paths = ["lib"]
18
+
19
+ spec.add_dependency "fluentd", ">= 0.14.0", "< 2"
20
+ spec.add_dependency "aws-sdk", "~> 3"
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.7"
23
+ end
@@ -0,0 +1,14 @@
1
+ <source>
2
+ @type rds_pgsql_log
3
+ access_key_id XXXXXXXXXXXXXXXXXXXX
4
+ secret_access_key xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
5
+ region ap-northeast-1
6
+ db_instance_identifier test-postgres
7
+ refresh_interval 30
8
+ tag pgsql.log
9
+ pos_file /tmp/pgsql-log-pos.dat
10
+ </source>
11
+
12
+ <match pgsql.log>
13
+ @type stdout
14
+ </match>
@@ -0,0 +1,236 @@
1
+ require 'fluent/input'
2
+ require 'time'
3
+ require 'aws-sdk'
4
+
5
+ class Fluent::Plugin::RdsPgsqlLogInput < Fluent::Plugin::Input
6
+ Fluent::Plugin.register_input('rds_pgsql_log', self)
7
+
8
+ LOG_REGEXP = /^(?<time>\d{4}-\d{2}-\d{2} \d{2}\:\d{2}\:\d{2} .+?):(?<host>.*?):(?<user>.*?)@(?<database>.*?):\[(?<pid>.*?)\]:(?<message_level>.*?):(?<message>.*)$/
9
+
10
+ config_param :access_key_id, :string, :default => nil
11
+ config_param :secret_access_key, :string, :default => nil
12
+ config_param :region, :string, :default => nil
13
+ config_param :db_instance_identifier, :string, :default => nil
14
+ config_param :pos_file, :string, :default => "fluent-plugin-rds-pgsql-log-pos.dat"
15
+ config_param :refresh_interval, :integer, :default => 30
16
+ config_param :tag, :string, :default => "rds-pgsql.log"
17
+
18
+ def configure(conf)
19
+ super
20
+
21
+ raise Fluent::ConfigError.new("region is required") unless @region
22
+ if !has_iam_role?
23
+ raise Fluent::ConfigError.new("access_key_id is required") if @access_key_id.nil?
24
+ raise Fluent::ConfigError.new("secret_access_key is required") if @secret_access_key.nil?
25
+ end
26
+ raise Fluent::ConfigError.new("db_instance_identifier is required") unless @db_instance_identifier
27
+ raise Fluent::ConfigError.new("pos_file is required") unless @pos_file
28
+ raise Fluent::ConfigError.new("refresh_interval is required") unless @refresh_interval
29
+ raise Fluent::ConfigError.new("tag is required") unless @tag
30
+ end
31
+
32
+ def start
33
+ super
34
+
35
+ # pos file touch
36
+ File.open(@pos_file, File::RDWR|File::CREAT).close
37
+
38
+ begin
39
+ options = {
40
+ :region => @region,
41
+ }
42
+ if @access_key_id && @secret_access_key
43
+ options[:access_key_id] = @access_key_id
44
+ options[:secret_access_key] = @secret_access_key
45
+ end
46
+ @rds = Aws::RDS::Client.new(options)
47
+ rescue => e
48
+ $log.warn "RDS Client error occurred: #{e.message}"
49
+ end
50
+
51
+ @loop = Coolio::Loop.new
52
+ timer_trigger = TimerWatcher.new(@refresh_interval, true, &method(:input))
53
+ timer_trigger.attach(@loop)
54
+ @thread = Thread.new(&method(:run))
55
+ end
56
+
57
+ def shutdown
58
+ super
59
+ @loop.stop
60
+ @thread.join
61
+ end
62
+
63
+ private
64
+
65
+ def run
66
+ @loop.run
67
+ end
68
+
69
+ def input
70
+ get_and_parse_posfile
71
+ log_files = get_logfile_list
72
+ get_logfile(log_files)
73
+ put_posfile
74
+ end
75
+
76
+ def has_iam_role?
77
+ begin
78
+ ec2 = Aws::EC2::Client.new(region: @region)
79
+ !ec2.config.credentials.nil?
80
+ rescue => e
81
+ $log.warn "EC2 Client error occurred: #{e.message}"
82
+ end
83
+ end
84
+
85
+ def get_and_parse_posfile
86
+ begin
87
+ # get & parse pos file
88
+ $log.debug "pos file get start"
89
+
90
+ pos_last_written_timestamp = 0
91
+ pos_info = {}
92
+ File.open(@pos_file, File::RDONLY) do |file|
93
+ file.each_line do |line|
94
+
95
+ pos_match = /^(\d+)$/.match(line)
96
+ if pos_match
97
+ pos_last_written_timestamp = pos_match[1].to_i
98
+ $log.debug "pos_last_written_timestamp: #{pos_last_written_timestamp}"
99
+ end
100
+
101
+ pos_match = /^(.+)\t(.+)$/.match(line)
102
+ if pos_match
103
+ pos_info[pos_match[1]] = pos_match[2]
104
+ $log.debug "log_file: #{pos_match[1]}, marker: #{pos_match[2]}"
105
+ end
106
+ end
107
+ @pos_last_written_timestamp = pos_last_written_timestamp
108
+ @pos_info = pos_info
109
+ end
110
+ rescue => e
111
+ $log.warn "pos file get and parse error occurred: #{e.message}"
112
+ end
113
+ end
114
+
115
+ def put_posfile
116
+ # pos file write
117
+ begin
118
+ $log.debug "pos file write"
119
+ File.open(@pos_file, File::WRONLY|File::TRUNC) do |file|
120
+ file.puts @pos_last_written_timestamp.to_s
121
+
122
+ @pos_info.each do |log_file_name, marker|
123
+ file.puts "#{log_file_name}\t#{marker}"
124
+ end
125
+ end
126
+ rescue => e
127
+ $log.warn "pos file write error occurred: #{e.message}"
128
+ end
129
+ end
130
+
131
+ def get_logfile_list
132
+ begin
133
+ $log.debug "get logfile-list from rds: db_instance_identifier=#{@db_instance_identifier}, pos_last_written_timestamp=#{@pos_last_written_timestamp}"
134
+ @rds.describe_db_log_files(
135
+ db_instance_identifier: @db_instance_identifier,
136
+ file_last_written: @pos_last_written_timestamp,
137
+ max_records: 10,
138
+ )
139
+ rescue => e
140
+ $log.warn "RDS Client describe_db_log_files error occurred: #{e.message}"
141
+ end
142
+ end
143
+
144
+ def get_logfile(log_files)
145
+ begin
146
+ log_files.each do |log_file|
147
+ log_file.describe_db_log_files.each do |item|
148
+ # save maximum written timestamp value
149
+ @pos_last_written_timestamp = item[:last_written] if @pos_last_written_timestamp < item[:last_written]
150
+
151
+ # log file download
152
+ log_file_name = item[:log_file_name]
153
+ marker = @pos_info.has_key?(log_file_name) ? @pos_info[log_file_name] : "0"
154
+
155
+ $log.debug "download log from rds: log_file_name=#{log_file_name}, marker=#{marker}"
156
+ logs = @rds.download_db_log_file_portion(
157
+ db_instance_identifier: @db_instance_identifier,
158
+ log_file_name: log_file_name,
159
+ marker: marker,
160
+ )
161
+ raw_records = get_logdata(logs)
162
+
163
+ #emit
164
+ parse_and_emit(raw_records, log_file_name) unless raw_records.nil?
165
+ end
166
+ end
167
+ rescue => e
168
+ $log.warn e.message
169
+ end
170
+ end
171
+
172
+ def get_logdata(logs)
173
+ log_file_name = logs.context.params[:log_file_name]
174
+ raw_records = []
175
+ begin
176
+ logs.each do |log|
177
+ # save got line's marker
178
+ @pos_info[log_file_name] = log.marker
179
+
180
+ raw_records += log.log_file_data.split("\n")
181
+ end
182
+ rescue => e
183
+ $log.warn e.message
184
+ end
185
+ return raw_records
186
+ end
187
+
188
+ def parse_and_emit(raw_records, log_file_name)
189
+ begin
190
+ $log.debug "raw_records.count: #{raw_records.count}"
191
+ record = nil
192
+ raw_records.each do |raw_record|
193
+ $log.debug "raw_record=#{raw_record}"
194
+ line_match = LOG_REGEXP.match(raw_record)
195
+
196
+ unless line_match
197
+ # combine chain of log
198
+ record["message"] << "\n" + raw_record unless record.nil?
199
+ else
200
+ # emit before record
201
+ router.emit(@tag, record["unix_timestamp"], record) unless record.nil?
202
+
203
+ # set a record
204
+ unix_timestamp = Time.parse(line_match[:time]).to_i
205
+ record = {
206
+ "time" => line_match[:time],
207
+ "unix_timestamp" => unix_timestamp,
208
+ "host" => line_match[:host],
209
+ "user" => line_match[:user],
210
+ "database" => line_match[:database],
211
+ "pid" => line_match[:pid],
212
+ "LOGLEVEL" => line_match[:message_level],
213
+ "message" => line_match[:message],
214
+ "log_file_name" => log_file_name,
215
+ }
216
+ end
217
+ end
218
+ # emit last record
219
+ router.emit(@tag, record["unix_timestamp"], record) unless record.nil?
220
+ rescue => e
221
+ $log.warn e.message
222
+ end
223
+ end
224
+
225
+ class TimerWatcher < Coolio::TimerWatcher
226
+ def initialize(interval, repeat, &callback)
227
+ @callback = callback
228
+ on_timer # first call
229
+ super(interval, repeat)
230
+ end
231
+
232
+ def on_timer
233
+ @callback.call
234
+ end
235
+ end
236
+ end
metadata ADDED
@@ -0,0 +1,102 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: belong-plugin-rds-pgsql-log
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.1
5
+ platform: ruby
6
+ authors:
7
+ - shinsaka
8
+ - phani
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2018-06-18 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: fluentd
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: 0.14.0
21
+ - - "<"
22
+ - !ruby/object:Gem::Version
23
+ version: '2'
24
+ type: :runtime
25
+ prerelease: false
26
+ version_requirements: !ruby/object:Gem::Requirement
27
+ requirements:
28
+ - - ">="
29
+ - !ruby/object:Gem::Version
30
+ version: 0.14.0
31
+ - - "<"
32
+ - !ruby/object:Gem::Version
33
+ version: '2'
34
+ - !ruby/object:Gem::Dependency
35
+ name: aws-sdk
36
+ requirement: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3'
41
+ type: :runtime
42
+ prerelease: false
43
+ version_requirements: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3'
48
+ - !ruby/object:Gem::Dependency
49
+ name: bundler
50
+ requirement: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.7'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.7'
62
+ description: fluentd plugin for Amazon RDS for PostgreSQL log input with a fix for
63
+ timestamp
64
+ email:
65
+ - shinx1265@gmail.com
66
+ - phani@belong.co
67
+ executables: []
68
+ extensions: []
69
+ extra_rdoc_files: []
70
+ files:
71
+ - ".gitignore"
72
+ - Gemfile
73
+ - LICENSE.txt
74
+ - README.md
75
+ - belong-plugin-rds-pgsql-log.gemspec
76
+ - fluent.conf.sample
77
+ - lib/fluent/plugin/in_rds_pgsql_log.rb
78
+ homepage: https://github.com/belongco/fluent-plugin-rds-pgsql-log
79
+ licenses:
80
+ - MIT
81
+ metadata: {}
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ requirements: []
97
+ rubyforge_project:
98
+ rubygems_version: 2.5.2.1
99
+ signing_key:
100
+ specification_version: 4
101
+ summary: Amazon RDS for PostgreSQL log input plugin
102
+ test_files: []