fluent-plugin-rds-pgsql-log 0.1.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 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: []