fluentd-plugin-kinesis-intuit 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,170 @@
1
+ #
2
+ # Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License"). You
5
+ # may not use this file except in compliance with the License. A copy of
6
+ # the License is located at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # or in the "license" file accompanying this file. This file is
11
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
12
+ # ANY KIND, either express or implied. See the License for the specific
13
+ # language governing permissions and limitations under the License.
14
+
15
+ require 'fluent/configurable'
16
+ require 'aws-sdk-core'
17
+
18
+ module Fluent
19
+ module Plugin
20
+ module KinesisHelper
21
+ module Client
22
+ module ClientParams
23
+ include Fluent::Configurable
24
+ config_param :region, :string, default: nil
25
+
26
+ config_param :http_proxy, :string, default: nil, secret: true
27
+ config_param :endpoint, :string, default: nil
28
+ config_param :ssl_verify_peer, :bool, default: true
29
+
30
+ config_param :aws_key_id, :string, default: nil, secret: true
31
+ config_param :aws_sec_key, :string, default: nil, secret: true
32
+ config_section :assume_role_credentials, multi: false do
33
+ desc "The Amazon Resource Name (ARN) of the role to assume"
34
+ config_param :role_arn, :string, secret: true
35
+ desc "An identifier for the assumed role session"
36
+ config_param :role_session_name, :string
37
+ desc "An IAM policy in JSON format"
38
+ config_param :policy, :string, default: nil
39
+ desc "The duration, in seconds, of the role session (900-3600)"
40
+ config_param :duration_seconds, :integer, default: nil
41
+ desc "A unique identifier that is used by third parties when assuming roles in their customers' accounts."
42
+ config_param :external_id, :string, default: nil, secret: true
43
+ desc "A http proxy url for requests to aws sts service"
44
+ config_param :sts_http_proxy, :string, default: nil, secret: true
45
+ end
46
+ config_section :instance_profile_credentials, multi: false do
47
+ desc "Number of times to retry when retrieving credentials"
48
+ config_param :retries, :integer, default: nil
49
+ desc "IP address (default:169.254.169.254)"
50
+ config_param :ip_address, :string, default: nil
51
+ desc "Port number (default:80)"
52
+ config_param :port, :integer, default: nil
53
+ desc "Number of seconds to wait for the connection to open"
54
+ config_param :http_open_timeout, :float, default: nil
55
+ desc "Number of seconds to wait for one block to be read"
56
+ config_param :http_read_timeout, :float, default: nil
57
+ # config_param :delay, :integer or :proc, :default => nil
58
+ # config_param :http_degub_output, :io, :default => nil
59
+ end
60
+ config_section :shared_credentials, multi: false do
61
+ desc "Path to the shared file. (default: $HOME/.aws/credentials)"
62
+ config_param :path, :string, default: nil
63
+ desc "Profile name. Default to 'default' or ENV['AWS_PROFILE']"
64
+ config_param :profile_name, :string, default: nil
65
+ end
66
+ end
67
+
68
+ def self.included(mod)
69
+ mod.include ClientParams
70
+ end
71
+
72
+ def configure(conf)
73
+ super
74
+ @region = client.config.region if @region.nil?
75
+ end
76
+
77
+ def client
78
+ @client ||= client_class.new(client_options)
79
+ end
80
+
81
+ private
82
+
83
+ def aws_sdk_v2?
84
+ @aws_sdk_v2 ||= Gem.loaded_specs['aws-sdk-core'].version < Gem::Version.create('3')
85
+ end
86
+
87
+ def client_class
88
+ case request_type
89
+ when :streams, :streams_aggregated
90
+ if aws_sdk_v2?
91
+ require 'aws-sdk'
92
+ else
93
+ require 'aws-sdk-kinesis'
94
+ end
95
+ Aws::Kinesis::Client
96
+ when :firehose
97
+ if aws_sdk_v2?
98
+ require 'aws-sdk'
99
+ else
100
+ require 'aws-sdk-firehose'
101
+ end
102
+ Aws::Firehose::Client
103
+ end
104
+ end
105
+
106
+ def client_options
107
+ options = setup_credentials
108
+ options.update(
109
+ user_agent_suffix: "fluent-plugin-kinesis/#{request_type}/#{FluentPluginKinesis::VERSION}"
110
+ )
111
+ options.update(region: @region) unless @region.nil?
112
+ options.update(http_proxy: @http_proxy) unless @http_proxy.nil?
113
+ options.update(endpoint: @endpoint) unless @endpoint.nil?
114
+ options.update(ssl_verify_peer: @ssl_verify_peer) unless @ssl_verify_peer.nil?
115
+ if @debug
116
+ options.update(logger: Logger.new(log.out))
117
+ options.update(log_level: :debug)
118
+ end
119
+ options
120
+ end
121
+
122
+ def setup_credentials
123
+ options = {}
124
+ credentials_options = {}
125
+ case
126
+ when @aws_key_id && @aws_sec_key
127
+ options[:access_key_id] = @aws_key_id
128
+ options[:secret_access_key] = @aws_sec_key
129
+ when @assume_role_credentials
130
+ c = @assume_role_credentials
131
+ credentials_options[:role_arn] = c.role_arn
132
+ credentials_options[:role_session_name] = c.role_session_name
133
+ credentials_options[:policy] = c.policy if c.policy
134
+ credentials_options[:duration_seconds] = c.duration_seconds if c.duration_seconds
135
+ credentials_options[:external_id] = c.external_id if c.external_id
136
+ if c.sts_http_proxy and @region
137
+ credentials_options[:client] = Aws::STS::Client.new(region: @region, http_proxy: c.sts_http_proxy)
138
+ elsif @region
139
+ credentials_options[:client] = Aws::STS::Client.new(region: @region)
140
+ elsif c.sts_http_proxy
141
+ credentials_options[:client] = Aws::STS::Client.new(http_proxy: c.sts_http_proxy)
142
+ end
143
+ options[:credentials] = Aws::AssumeRoleCredentials.new(credentials_options)
144
+ when @instance_profile_credentials
145
+ c = @instance_profile_credentials
146
+ credentials_options[:retries] = c.retries if c.retries
147
+ credentials_options[:ip_address] = c.ip_address if c.ip_address
148
+ credentials_options[:port] = c.port if c.port
149
+ credentials_options[:http_open_timeout] = c.http_open_timeout if c.http_open_timeout
150
+ credentials_options[:http_read_timeout] = c.http_read_timeout if c.http_read_timeout
151
+ if ENV["AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"]
152
+ options[:credentials] = Aws::ECSCredentials.new(credentials_options)
153
+ else
154
+ options[:credentials] = Aws::InstanceProfileCredentials.new(credentials_options)
155
+ end
156
+ when @shared_credentials
157
+ c = @shared_credentials
158
+ credentials_options[:path] = c.path if c.path
159
+ credentials_options[:profile_name] = c.profile_name if c.profile_name
160
+ options[:credentials] = Aws::SharedCredentials.new(credentials_options)
161
+ else
162
+ # Use default credentials
163
+ # See http://docs.aws.amazon.com/sdkforruby/api/Aws/S3/Client.html
164
+ end
165
+ options
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,59 @@
1
+ #
2
+ # Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License"). You
5
+ # may not use this file except in compliance with the License. A copy of
6
+ # the License is located at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # or in the "license" file accompanying this file. This file is
11
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
12
+ # ANY KIND, either express or implied. See the License for the specific
13
+ # language governing permissions and limitations under the License.
14
+
15
+ require 'fluent/plugin/kinesis'
16
+
17
+ module Fluent
18
+ module Plugin
19
+ class KinesisFirehoseOutput < KinesisOutput
20
+ Fluent::Plugin.register_output('kinesis_firehose', self)
21
+
22
+ RequestType = :firehose
23
+ BatchRequestLimitCount = 500
24
+ BatchRequestLimitSize = 4 * 1024 * 1024
25
+ include KinesisHelper::API::BatchRequest
26
+
27
+ config_param :delivery_stream_name, :string
28
+ config_param :append_new_line, :bool, default: true
29
+
30
+ def configure(conf)
31
+ super
32
+ if @append_new_line
33
+ org_data_formatter = @data_formatter
34
+ @data_formatter = ->(tag, time, record) {
35
+ org_data_formatter.call(tag, time, record) + "\n"
36
+ }
37
+ end
38
+ end
39
+
40
+ def format(tag, time, record)
41
+ format_for_api do
42
+ [@data_formatter.call(tag, time, record)]
43
+ end
44
+ end
45
+
46
+ def write(chunk)
47
+ write_records_batch(chunk) do |batch|
48
+ records = batch.map{|(data)|
49
+ { data: data }
50
+ }
51
+ client.put_record_batch(
52
+ delivery_stream_name: @delivery_stream_name,
53
+ records: records,
54
+ )
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,160 @@
1
+ #
2
+ # Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License"). You
5
+ # may not use this file except in compliance with the License. A copy of
6
+ # the License is located at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # or in the "license" file accompanying this file. This file is
11
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
12
+ # ANY KIND, either express or implied. See the License for the specific
13
+ # language governing permissions and limitations under the License.
14
+
15
+ require 'fluent/plugin/kinesis'
16
+
17
+ module Fluent
18
+ module Plugin
19
+ class KinesisStreamsOutput < KinesisOutput
20
+ Fluent::Plugin.register_output('kinesis_streams', self)
21
+
22
+ RequestType = :streams
23
+ BatchRequestLimitCount = 500
24
+ BatchRequestLimitSize = 5 * 1024 * 1024
25
+ include KinesisHelper::API::BatchRequest
26
+
27
+ config_param :stream_name, :string
28
+ config_param :partition_key, :string, default: nil
29
+
30
+ desc 'The Splunk index to index events. When not set, will be decided by HEC'
31
+ config_param :index, :string, default: nil
32
+
33
+ desc 'The host field for events, by default it uses the hostname of the machine that runnning fluentd. '
34
+ config_param :host, :string, default: nil
35
+
36
+ desc 'The source field for events, when not set, will be decided by HEC.'
37
+ config_param :source, :string, default: nil
38
+
39
+ desc 'The sourcetype field for events, when not set, will be decided by HEC.'
40
+ config_param :sourcetype, :string, default: nil
41
+
42
+ desc 'All items in the record will be sent if true, otherwise only message'
43
+ config_param :all_items, :bool, :default => false
44
+
45
+ def initialize
46
+ super
47
+ @default_host = Socket.gethostname
48
+ end
49
+
50
+ # Thanks to
51
+ # https://github.com/kazegusuri/fluent-plugin-prometheus/blob/348c112d/lib/fluent/plugin/prometheus.rb
52
+ def self.placeholder_expander(log)
53
+ # Use internal class in order to expand placeholder
54
+ if defined?(Fluent::Filter) # for v0.12, built-in PlaceholderExpander
55
+ begin
56
+ require 'fluent/plugin/filter_record_transformer'
57
+ if defined?(Fluent::Plugin::RecordTransformerFilter::PlaceholderExpander)
58
+ # for v0.14
59
+ return Fluent::Plugin::RecordTransformerFilter::PlaceholderExpander.new(log: log)
60
+ else
61
+ # for v0.12
62
+ return Fluent::RecordTransformerFilter::PlaceholderExpander.new(log: log)
63
+ end
64
+ rescue LoadError => e
65
+ raise ConfigError, "cannot find filter_record_transformer plugin: #{e.message}"
66
+ end
67
+ else # for v0.10, use PlaceholderExapander in fluent-plugin-record-reformer plugin
68
+ begin
69
+ require 'fluent/plugin/out_record_reformer.rb'
70
+ return Fluent::RecordReformerOutput::PlaceholderExpander.new(log: log)
71
+ rescue LoadError => e
72
+ raise ConfigError, "cannot find fluent-plugin-record-reformer: #{e.message}"
73
+ end
74
+ end
75
+ end
76
+
77
+ def configure(conf)
78
+ super
79
+ # check_conflict
80
+ # prepare_key_fields
81
+ @placeholder_expander = Fluent::Plugin::KinesisStreamsOutput.placeholder_expander(log)
82
+ @hostname = Socket.gethostname
83
+ @key_formatter = key_formatter_create
84
+ end
85
+
86
+
87
+ def format(tag, time, record)
88
+
89
+ placeholder_values = {
90
+ 'tag' => tag,
91
+ 'tag_parts' => tag.split('.'),
92
+ 'hostname' => @default_host,
93
+ 'record' => record
94
+ }
95
+
96
+ placeholders = @placeholder_expander.prepare_placeholders(placeholder_values)
97
+
98
+ payload = {
99
+ # for v0.14 millisecs time precision
100
+ time: time.is_a?(Integer) ? time.to_i : time.to_f,
101
+ source: @source.nil? ? tag.to_s : @placeholder_expander.expand(@source, placeholders),
102
+ sourcetype: @placeholder_expander.expand(@sourcetype.to_s, placeholders),
103
+ host: @host.nil? ? @default_host : @placeholder_expander.expand(@host.to_s, placeholders),
104
+ index: @placeholder_expander.expand(@index.to_s, placeholders)
105
+ }
106
+ #payload[:index] = @index if @index
107
+ #payload[:source] = @source if @source
108
+ #payload[:sourcetype] = @sourcetype if @sourcetype
109
+
110
+ # delete nil fields otherwise will get formet error from HEC
111
+ %i[host index source sourcetype].each { |f| payload.delete f if payload[f].nil? }
112
+
113
+ if !record[@data_key].nil?
114
+ payload[@data_key] = record[@data_key]
115
+ end
116
+
117
+ if @all_items
118
+ payload[:event] = record
119
+ else
120
+ payload[:event] = record["message"]
121
+ end
122
+
123
+ format_for_api do
124
+ data = @data_formatter.call(tag, time, payload)
125
+ key = @key_formatter.call(payload)
126
+ [data, key]
127
+ end
128
+ end
129
+
130
+ def write(chunk)
131
+ write_records_batch(chunk) do |batch|
132
+ records = batch.map{|(data, partition_key)|
133
+ { data: data, partition_key: partition_key }
134
+ }
135
+ client.put_records(
136
+ stream_name: @stream_name,
137
+ records: records,
138
+ )
139
+ end
140
+ end
141
+
142
+ private
143
+
144
+ def key_formatter_create
145
+ if @partition_key.nil?
146
+ ->(record) { SecureRandom.hex(16) }
147
+ else
148
+ ->(record) {
149
+ if !record.key?(@partition_key)
150
+ raise KeyNotFoundError.new(@partition_key, record)
151
+ end
152
+ record[@partition_key]
153
+ }
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
159
+
160
+
@@ -0,0 +1,78 @@
1
+ #
2
+ # Copyright 2014-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License"). You
5
+ # may not use this file except in compliance with the License. A copy of
6
+ # the License is located at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # or in the "license" file accompanying this file. This file is
11
+ # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
12
+ # ANY KIND, either express or implied. See the License for the specific
13
+ # language governing permissions and limitations under the License.
14
+
15
+ require 'fluent/plugin/kinesis'
16
+ require 'fluent/plugin/kinesis_helper/aggregator'
17
+
18
+ module Fluent
19
+ module Plugin
20
+ class KinesisStreamsAggregatedOutput < KinesisOutput
21
+ Fluent::Plugin.register_output('kinesis_streams_aggregated', self)
22
+ include KinesisHelper::Aggregator::Mixin
23
+
24
+ RequestType = :streams_aggregated
25
+ BatchRequestLimitCount = 100_000
26
+ BatchRequestLimitSize = 1024 * 1024
27
+ include KinesisHelper::API::BatchRequest
28
+
29
+ config_param :stream_name, :string
30
+ config_param :fixed_partition_key, :string, default: nil
31
+
32
+ def configure(conf)
33
+ super
34
+ @partition_key_generator = create_partition_key_generator
35
+ @batch_request_max_size -= offset
36
+ @max_record_size -= offset
37
+ end
38
+
39
+ def format(tag, time, record)
40
+ format_for_api do
41
+ [@data_formatter.call(tag, time, record)]
42
+ end
43
+ end
44
+
45
+ def write(chunk)
46
+ write_records_batch(chunk) do |batch|
47
+ key = @partition_key_generator.call
48
+ records = batch.map{|(data)|data}
49
+ client.put_records(
50
+ stream_name: @stream_name,
51
+ records: [{
52
+ partition_key: key,
53
+ data: aggregator.aggregate(records, key),
54
+ }],
55
+ )
56
+ end
57
+ end
58
+
59
+ def offset
60
+ @offset ||= AggregateOffset + @partition_key_generator.call.size*2
61
+ end
62
+
63
+ private
64
+
65
+ def size_of_values(record)
66
+ super(record) + RecordOffset
67
+ end
68
+
69
+ def create_partition_key_generator
70
+ if @fixed_partition_key.nil?
71
+ ->() { SecureRandom.hex(16) }
72
+ else
73
+ ->() { @fixed_partition_key }
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end