belong-plugin-rds-pgsql-log 0.3.1

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