fluent-plugin-rds-pgsql-log 0.1.0

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: 490d5c629a6533d73d1c2a85ad7ccf8962ac6d30
4
+ data.tar.gz: e892a87af285abe6a941ce5387f52507bb9569eb
5
+ SHA512:
6
+ metadata.gz: 04b299e27b14de7411e573cdfc3d4d16579a3ee069e1afaab07e04ff2e7e2819fdaf35a0d1edf4d8048d277211bc50f5125e519e39582a559c39b630d38c6078
7
+ data.tar.gz: 1bbd9858e9b20f54fcc8f93da6cb61be9ef987f7dfc8cf4c74b017567e540bb0934ddd3f476d1117788df05046741fda1db205ddd56d25cb871d56c382f1f9f0
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,72 @@
1
+ # Amazon RDS for PostgreSQL log input plugin for fluentd
2
+
3
+ ## Overview
4
+ - Amazon Web Services RDS log input plugin for fluentd
5
+
6
+ ## Installation
7
+
8
+ $ fluentd-gem fluent-plugin-rds-pgsql-log
9
+
10
+ ## AWS ELB Settings
11
+ - settings see: [PostgreSQL Database Log Files](http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_LogAccess.Concepts.PostgreSQL.html)
12
+
13
+ ## When SSL certification error
14
+ log:
15
+ ```
16
+ SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed
17
+ ```
18
+ Do env setting follows:
19
+ ```
20
+ SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt (If you using amazon linux)
21
+ ```
22
+
23
+ ## Configuration
24
+
25
+ ```config
26
+ <source>
27
+ type rds_pgsql_log
28
+ # required
29
+ region <region name>
30
+ db_instance_identifier <instance identifier>
31
+ # optional if you can IAM credentials
32
+ access_key_id <access_key>
33
+ secret_access_key <secret_access_key>
34
+ # optional
35
+ refresh_interval <interval number by second(default: 30)>
36
+ tag <tag name(default: rds-pgsql.log>
37
+ pos_file <log getting position file(default: rds-pgsql.log)>
38
+ </source>
39
+ ```
40
+
41
+ ### Example setting
42
+ ```config
43
+ <source>
44
+ type rds_pgsql_log
45
+ region ap-northeast-1
46
+ db_instance_identifier test-postgres
47
+ access_key_id XXXXXXXXXXXXXXXXXXXX
48
+ secret_access_key xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
49
+ refresh_interval 30
50
+ tag pgsql.log
51
+ pos_file /tmp/pgsql-log-pos.dat
52
+ </source>
53
+
54
+ <match pgsql.log>
55
+ type stdout
56
+ </match>
57
+ ```
58
+
59
+ ### json output example
60
+ ```
61
+
62
+ {"time":"2015-05-30 02:19:22 UTC",
63
+ "host":"192.168.30.175(53092)",
64
+ "user":"testuser",
65
+ "database":"db1",
66
+ "pid":"9769",
67
+ "message_level":"LOG",
68
+ "message":" statement: select 1;",
69
+ "log_file_name":"error/postgresql.log.2015-05-30-02"
70
+ }
71
+ ```
72
+
@@ -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 = "fluent-plugin-rds-pgsql-log"
6
+ spec.version = "0.1.0"
7
+ spec.authors = ["shinsaka"]
8
+ spec.email = ["shinx1265@gmail.com"]
9
+ spec.summary = "Amazon RDS for PostgreSQL log input plugin"
10
+ spec.description = "fluentd plugin for Amazon RDS for PostgreSQL log input"
11
+ spec.homepage = "https://github.com/shinsaka/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"
20
+ spec.add_dependency "aws-sdk", "~> 2"
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,232 @@
1
+ class Fluent::RdsPgsqlLogInput < Fluent::Input
2
+ Fluent::Plugin.register_input('rds_pgsql_log', self)
3
+
4
+ LOG_REGEXP = /^(?<time>\d{4}-\d{2}-\d{2} \d{2}\:\d{2}\:\d{2} .+?):(?<host>.*?):(?<user>.*?)@(?<database>.*?):\[(?<pid>.*?)\]:(?<message_level>.*?):(?<message>.*)$/
5
+
6
+ config_param :access_key_id, :string, :default => nil
7
+ config_param :secret_access_key, :string, :default => nil
8
+ config_param :region, :string, :default => nil
9
+ config_param :db_instance_identifier, :string, :default => nil
10
+ config_param :pos_file, :string, :default => "fluent-plugin-rds-pgsql-log-pos.dat"
11
+ config_param :refresh_interval, :integer, :default => 30
12
+ config_param :tag, :string, :default => "rds-pgsql.log"
13
+
14
+ def configure(conf)
15
+ super
16
+ require 'aws-sdk'
17
+
18
+ if !has_iam_role?
19
+ raise Fluent::ConfigError.new("access_key_id is required") if @access_key_id.nil?
20
+ raise Fluent::ConfigError.new("secret_access_key is required") if @secret_access_key.nil?
21
+ end
22
+ raise Fluent::ConfigError.new("region is required") unless @region
23
+ raise Fluent::ConfigError.new("db_instance_identifier is required") unless @db_instance_identifier
24
+ raise Fluent::ConfigError.new("pos_file is required") unless @pos_file
25
+ raise Fluent::ConfigError.new("refresh_interval is required") unless @refresh_interval
26
+ raise Fluent::ConfigError.new("tag is required") unless @tag
27
+ end
28
+
29
+ def start
30
+ super
31
+
32
+ # pos file touch
33
+ File.open(@pos_file, File::RDWR|File::CREAT).close
34
+
35
+ begin
36
+ options = {
37
+ :region => @region,
38
+ }
39
+ if @access_key_id && @secret_access_key
40
+ options[:access_key_id] = @access_key_id
41
+ options[:secret_access_key] = @secret_access_key
42
+ end
43
+ @rds = Aws::RDS::Client.new(options)
44
+ rescue => e
45
+ $log.warn "RDS Client error occurred: #{e.message}"
46
+ end
47
+
48
+ @loop = Coolio::Loop.new
49
+ timer_trigger = TimerWatcher.new(@refresh_interval, true, &method(:input))
50
+ timer_trigger.attach(@loop)
51
+ @thread = Thread.new(&method(:run))
52
+ end
53
+
54
+ def shutdown
55
+ super
56
+ @loop.stop
57
+ @thread.join
58
+ @timestamp_file.close
59
+ end
60
+
61
+ private
62
+
63
+ def run
64
+ @loop.run
65
+ end
66
+
67
+ def input
68
+ get_and_parse_posfile
69
+ log_files = get_logfile_list
70
+ get_logfile(log_files)
71
+ put_posfile
72
+ end
73
+
74
+ def has_iam_role?
75
+ begin
76
+ ec2 = Aws::EC2::Client.new(region: @region)
77
+ !ec2.config.credentials.nil?
78
+ rescue => e
79
+ $log.warn "EC2 Client error occurred: #{e.message}"
80
+ end
81
+ end
82
+
83
+ def get_and_parse_posfile
84
+ begin
85
+ # get & parse pos file
86
+ $log.debug "pos file get start"
87
+
88
+ pos_last_written_timestamp = 0
89
+ pos_info = {}
90
+ File.open(@pos_file, File::RDONLY) do |file|
91
+ file.each_line do |line|
92
+
93
+ pos_match = /^(\d+)$/.match(line)
94
+ if pos_match
95
+ pos_last_written_timestamp = pos_match[1].to_i
96
+ $log.debug "pos_last_written_timestamp: #{pos_last_written_timestamp}"
97
+ end
98
+
99
+ pos_match = /^(.+)\t(.+)$/.match(line)
100
+ if pos_match
101
+ pos_info[pos_match[1]] = pos_match[2]
102
+ $log.debug "log_file: #{pos_match[1]}, marker: #{pos_match[2]}"
103
+ end
104
+ end
105
+ @pos_last_written_timestamp = pos_last_written_timestamp
106
+ @pos_info = pos_info
107
+ end
108
+ rescue => e
109
+ $log.warn "pos file get and parse error occurred: #{e.message}"
110
+ end
111
+ end
112
+
113
+ def put_posfile
114
+ # pos file write
115
+ begin
116
+ $log.debug "pos file write"
117
+ File.open(@pos_file, File::WRONLY|File::TRUNC) do |file|
118
+ file.puts @pos_last_written_timestamp.to_s
119
+
120
+ @pos_info.each do |log_file_name, marker|
121
+ file.puts "#{log_file_name}\t#{marker}"
122
+ end
123
+ end
124
+ rescue => e
125
+ $log.warn "pos file write error occurred: #{e.message}"
126
+ end
127
+ end
128
+
129
+ def get_logfile_list
130
+ begin
131
+ $log.debug "get logfile-list from rds: db_instance_identifier=#{@db_instance_identifier}, pos_last_written_timestamp=#{@pos_last_written_timestamp}"
132
+ @rds.describe_db_log_files(
133
+ db_instance_identifier: @db_instance_identifier,
134
+ file_last_written: @pos_last_written_timestamp,
135
+ max_records: 10,
136
+ )
137
+ rescue => e
138
+ $log.warn "RDS Client describe_db_log_files error occurred: #{e.message}"
139
+ end
140
+ end
141
+
142
+ def get_logfile(log_files)
143
+ begin
144
+ log_files.each do |log_file|
145
+ log_file.describe_db_log_files.each do |item|
146
+ # save maximum written timestamp value
147
+ @pos_last_written_timestamp = item[:last_written] if @pos_last_written_timestamp < item[:last_written]
148
+
149
+ # log file download
150
+ log_file_name = item[:log_file_name]
151
+ marker = @pos_info.has_key?(log_file_name) ? @pos_info[log_file_name] : "0"
152
+
153
+ $log.debug "download log from rds: log_file_name=#{log_file_name}, marker=#{marker}"
154
+ logs = @rds.download_db_log_file_portion(
155
+ db_instance_identifier: @db_instance_identifier,
156
+ log_file_name: log_file_name,
157
+ marker: marker,
158
+ )
159
+ raw_records = get_logdata(logs)
160
+
161
+ #emit
162
+ parse_and_emit(raw_records, log_file_name) unless raw_records.nil?
163
+ end
164
+ end
165
+ rescue => e
166
+ $log.warn e.message
167
+ end
168
+ end
169
+
170
+ def get_logdata(logs)
171
+ log_file_name = logs.context.params[:log_file_name]
172
+ raw_records = []
173
+ begin
174
+ logs.each do |log|
175
+ # save got line's marker
176
+ @pos_info[log_file_name] = log.marker
177
+
178
+ raw_records += log.log_file_data.split("\n")
179
+ end
180
+ rescue => e
181
+ $log.warn e.message
182
+ end
183
+ return raw_records
184
+ end
185
+
186
+ def parse_and_emit(raw_records, log_file_name)
187
+ begin
188
+ $log.debug "raw_records.count: #{raw_records.count}"
189
+ record = nil
190
+ raw_records.each do |raw_record|
191
+ $log.debug "raw_record=#{raw_record}"
192
+ line_match = LOG_REGEXP.match(raw_record)
193
+
194
+ unless line_match
195
+ # combine chain of log
196
+ record["message"] << "\n" + raw_record unless record.nil?
197
+ else
198
+ # emit before record
199
+ Fluent::Engine.emit(@tag, Fluent::Engine.now, record) unless record.nil?
200
+
201
+ # set a record
202
+ record = {
203
+ "time" => line_match[:time],
204
+ "host" => line_match[:host],
205
+ "user" => line_match[:user],
206
+ "database" => line_match[:database],
207
+ "pid" => line_match[:pid],
208
+ "message_level" => line_match[:message_level],
209
+ "message" => line_match[:message],
210
+ "log_file_name" => log_file_name,
211
+ }
212
+ end
213
+ end
214
+ # emit last record
215
+ Fluent::Engine.emit(@tag, Fluent::Engine.now, record) unless record.nil?
216
+ rescue => e
217
+ $log.warn e.message
218
+ end
219
+ end
220
+
221
+ class TimerWatcher < Coolio::TimerWatcher
222
+ def initialize(interval, repeat, &callback)
223
+ @callback = callback
224
+ on_timer # first call
225
+ super(interval, repeat)
226
+ end
227
+
228
+ def on_timer
229
+ @callback.call
230
+ end
231
+ end
232
+ end
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fluent-plugin-rds-pgsql-log
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - shinsaka
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-05-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: fluentd
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: aws-sdk
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.7'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.7'
55
+ description: fluentd plugin for Amazon RDS for PostgreSQL log input
56
+ email:
57
+ - shinx1265@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - Gemfile
64
+ - LICENSE.txt
65
+ - README.md
66
+ - fluent-plugin-rds-pgsql-log.gemspec
67
+ - fluent.conf.sample
68
+ - lib/fluent/plugin/in_rds_pgsql_log.rb
69
+ homepage: https://github.com/shinsaka/fluent-plugin-rds-pgsql-log
70
+ licenses:
71
+ - MIT
72
+ metadata: {}
73
+ post_install_message:
74
+ rdoc_options: []
75
+ require_paths:
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ requirements: []
88
+ rubyforge_project:
89
+ rubygems_version: 2.2.1
90
+ signing_key:
91
+ specification_version: 4
92
+ summary: Amazon RDS for PostgreSQL log input plugin
93
+ test_files: []