fluent-plugin-elb-log 1.3.2 → 1.4.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 +4 -4
- data/README.md +77 -11
- data/fluent-plugin-elb-log.gemspec +3 -3
- data/fluent.conf.sample +12 -2
- data/lib/fluent/plugin/in_elb_log.rb +315 -43
- data/test/plugin/in_elb_log.rb +25 -23
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b90a7ba2cb9c1a065697787d2634625df53bb3f28b2f4a3d88c940071255b848
|
4
|
+
data.tar.gz: 899bbcf97b460fbf709f4aee85a6ab418c8d58cd5fb53f7aeabaf54fcb729511
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dabbbdc5f79f83ba6003057126d62e492da600584840d72d2240eaea1bcc532cf56d9177762b0aa1cdc055e88bfb1d7840f7a11c6b95861905df3dd4e28eedbe
|
7
|
+
data.tar.gz: 48d929caa219fc1b8ef6aa2c5e971cfe2ea1c1f1f6a556ff431b3a7e338eb0c8f9a8701f284aec9ec5e71a7c1304f6cf0d0e403edccfe9d6361324bc362b76a4
|
data/README.md
CHANGED
@@ -58,6 +58,9 @@ SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt (If you using amazon linux)
|
|
58
58
|
tag <tag name(default: elb.access)>
|
59
59
|
delete <boolean delete processed log files from S3(default: false)>
|
60
60
|
include_all_message <boolean (default:false)>
|
61
|
+
start_time <last timestamp to start from>
|
62
|
+
exclude_pattern_logfile_elb_name <exclude pattern for logfile_elb_name key>
|
63
|
+
use_sqs <boolean use SQS polling instead of iteratively processing the whole bucket>
|
61
64
|
|
62
65
|
# following attibutes are required if you don't use IAM Role
|
63
66
|
access_key_id <access_key>
|
@@ -65,6 +68,44 @@ SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt (If you using amazon linux)
|
|
65
68
|
</source>
|
66
69
|
```
|
67
70
|
|
71
|
+
`use_sqs` automatically creates an SQS queue named `fluent-plugin-elb-log-<current_timestamp>`
|
72
|
+
and sets up the `all object create event` S3 event notification for the chosen S3 bucket. Stopping fluentd deletes both autmatically.
|
73
|
+
To make it work, the following IAM policy should be attached to the instance IAM role or the user:
|
74
|
+
```
|
75
|
+
{
|
76
|
+
"Version": "2012-10-17",
|
77
|
+
"Statement": [
|
78
|
+
{
|
79
|
+
"Sid": "FluentdPermissions",
|
80
|
+
"Effect": "Allow",
|
81
|
+
"Action": [
|
82
|
+
"sqs:DeleteMessage",
|
83
|
+
"s3:GetObject",
|
84
|
+
"sqs:ReceiveMessage",
|
85
|
+
"sqs:DeleteQueue",
|
86
|
+
"sqs:GetQueueAttributes",
|
87
|
+
"s3:ListBucket",
|
88
|
+
"s3:PutBucketNotification",
|
89
|
+
"sqs:CreateQueue"
|
90
|
+
],
|
91
|
+
"Resource": [
|
92
|
+
"arn:aws:sqs:*:123456789012:fluent-plugin-elb-log-*",
|
93
|
+
"arn:aws:s3:::alb-logs-bucket/*",
|
94
|
+
"arn:aws:s3:::alb-logs-bucket"
|
95
|
+
]
|
96
|
+
}
|
97
|
+
]
|
98
|
+
}
|
99
|
+
```
|
100
|
+
When `use_sqs` is false:
|
101
|
+
- 300 seconds is a good value for the `refresh_interval`
|
102
|
+
- the plugin executes processing the whole S3 bucket (with the respect of `s3_prefix` and `start_time`/`timestamp_file`) every `refresh_interval`
|
103
|
+
|
104
|
+
When `use_sqs` is true:
|
105
|
+
- `refresh_interval` of 30-60 seconds should be fine.
|
106
|
+
- the plugin executes processing the whole S3 bucket (with the respect of `s3_prefix` and `start_time`/`timestamp_file`) only once (at start) and then
|
107
|
+
polls the SQS queue every `refresh_interval`.
|
108
|
+
|
68
109
|
### Example setting
|
69
110
|
```config
|
70
111
|
<source>
|
@@ -74,16 +115,39 @@ SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt (If you using amazon linux)
|
|
74
115
|
s3_prefix prodcution/web
|
75
116
|
timestamp_file /tmp/elb_last_at.dat
|
76
117
|
buf_file /tmp/fluentd-elblog.tmpfile
|
77
|
-
refresh_interval
|
118
|
+
refresh_interval 30
|
78
119
|
tag elb.access
|
79
120
|
delete false
|
80
121
|
include_all_message false
|
122
|
+
exclude_pattern_logfile_elb_name "^app\.(uat|qa)\."
|
123
|
+
start_time 2025-02-27T10:45:00
|
124
|
+
use_sqs true
|
81
125
|
access_key_id XXXXXXXXXXXXXXXXXXXX
|
82
126
|
secret_access_key xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
83
127
|
</source>
|
84
128
|
|
85
|
-
<
|
86
|
-
@type
|
129
|
+
<filter elb.access>
|
130
|
+
@type record_transformer
|
131
|
+
<record>
|
132
|
+
timestamp ${record["request_creation_time"]}
|
133
|
+
logfile_name ${record["key"]}
|
134
|
+
</record>
|
135
|
+
remove_keys prefix,logfile_date,logfile_elb_name,logfile_hash,logfile_timestamp,logfile_timestamp_unixtime,key,time,s3_last_modified_unixtime
|
136
|
+
</filter>
|
137
|
+
|
138
|
+
<match elb.access>
|
139
|
+
# @type stdout
|
140
|
+
@type opensearch
|
141
|
+
hosts node-1,node-2,node-3
|
142
|
+
port 9200
|
143
|
+
scheme https
|
144
|
+
logstash_format true
|
145
|
+
logstash_prefix alb-logs
|
146
|
+
user fluentd
|
147
|
+
password secret
|
148
|
+
ssl_verify true
|
149
|
+
ca_file /etc/fluentd/root-ca.pem
|
150
|
+
flush_interval 300s
|
87
151
|
</match>
|
88
152
|
```
|
89
153
|
|
@@ -96,7 +160,7 @@ SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt (If you using amazon linux)
|
|
96
160
|
"logfile_elb_name":"my-elb-name",
|
97
161
|
"elb_ip_address":"52.0.0.0",
|
98
162
|
"logfile_hash":"12squv5w",
|
99
|
-
"
|
163
|
+
"logfile_timestamp":"20150615T0400Z",
|
100
164
|
"key":"TEST/AWSLogs/123456789012/elasticloadbalancing/ap-northeast-1/2015/06/15/123456789012_elasticloadbalancing_ap-northeast-1_my-elb-name_20150615T0400Z_52.68.215.138_69squv5w.log",
|
101
165
|
"prefix":"TEST",
|
102
166
|
"elb_timestamp_unixtime":1434340800,
|
@@ -104,13 +168,13 @@ SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt (If you using amazon linux)
|
|
104
168
|
"elb":"my-elb-name",
|
105
169
|
"client":"54.1.1.1",
|
106
170
|
"client_port":"43759",
|
107
|
-
"
|
108
|
-
"
|
171
|
+
"target":"10.0.0.1",
|
172
|
+
"target_port":"80",
|
109
173
|
"request_processing_time":4.0e-05,
|
110
|
-
"
|
174
|
+
"target_processing_time":0.105048,
|
111
175
|
"response_processing_time":2.4e-05,
|
112
176
|
"elb_status_code":"200",
|
113
|
-
"
|
177
|
+
"target_status_code":"200",
|
114
178
|
"received_bytes":0,
|
115
179
|
"sent_bytes":4622,
|
116
180
|
"request_method":"GET",
|
@@ -129,8 +193,10 @@ SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt (If you using amazon linux)
|
|
129
193
|
"actions_executed": "forward",
|
130
194
|
"redirect_url": "-",
|
131
195
|
"error_reason": "-",
|
132
|
-
"
|
133
|
-
"
|
134
|
-
"
|
196
|
+
"target_port_list": "\"192.168.0.1:443\"",
|
197
|
+
"target_status_code_list": "\"301\"",
|
198
|
+
"classification": "-",
|
199
|
+
"classification_reason": "-",
|
200
|
+
"conn_trace_id": "TID_xxxxxxxxxxxxxxxxxxxxxxx"
|
135
201
|
}
|
136
202
|
```
|
@@ -4,12 +4,12 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = "fluent-plugin-elb-log"
|
7
|
-
spec.version = "1.
|
8
|
-
spec.authors = ["shinsaka"]
|
7
|
+
spec.version = "1.4.0"
|
8
|
+
spec.authors = ["shinsaka","jazzl0ver"]
|
9
9
|
spec.email = ["shinx1265@gmail.com"]
|
10
10
|
spec.summary = "Amazon ELB log input plugin"
|
11
11
|
spec.description = "Amazon ELB log input plugin for fluentd"
|
12
|
-
spec.homepage = "https://github.com/
|
12
|
+
spec.homepage = "https://github.com/jazzl0ver/fluent-plugin-elb-log"
|
13
13
|
spec.license = "MIT"
|
14
14
|
|
15
15
|
spec.files = `git ls-files -z`.split("\x0")
|
data/fluent.conf.sample
CHANGED
@@ -5,12 +5,22 @@
|
|
5
5
|
s3_prefix prodcution/web
|
6
6
|
timestamp_file /tmp/elb_last_at.dat
|
7
7
|
buf_file /tmp/fluentd-elblog.tmpfile
|
8
|
-
refresh_interval
|
8
|
+
refresh_interval 30
|
9
9
|
tag elb.access
|
10
|
+
use_sqs true
|
10
11
|
access_key_id XXXXXXXXXXXXXXXXXXXX
|
11
12
|
secret_access_key xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
12
13
|
</source>
|
13
14
|
|
15
|
+
<filter elb.access>
|
16
|
+
@type record_transformer
|
17
|
+
<record>
|
18
|
+
timestamp ${record["request_creation_time"]}
|
19
|
+
logfile_name ${record["key"]}
|
20
|
+
</record>
|
21
|
+
remove_keys prefix,logfile_date,logfile_elb_name,logfile_hash,logfile_timestamp,logfile_timestamp_unixtime,key,time,s3_last_modified_unixtime
|
22
|
+
</filter>
|
23
|
+
|
14
24
|
<match elb.access>
|
15
|
-
|
25
|
+
@type stdout
|
16
26
|
</match>
|
@@ -3,6 +3,7 @@ require 'zlib'
|
|
3
3
|
require 'fileutils'
|
4
4
|
require 'aws-sdk-s3'
|
5
5
|
require 'aws-sdk-ec2'
|
6
|
+
require 'aws-sdk-sqs'
|
6
7
|
require 'fluent/input'
|
7
8
|
require 'digest/sha1'
|
8
9
|
|
@@ -11,8 +12,8 @@ class Fluent::Plugin::Elb_LogInput < Fluent::Plugin::Input
|
|
11
12
|
|
12
13
|
helpers :timer
|
13
14
|
|
14
|
-
LOGFILE_REGEXP = /^((?<prefix>.+?)\/|)AWSLogs\/(?<account_id>[0-9]{12})\/elasticloadbalancing\/(?<region>.+?)\/(?<logfile_date>[0-9]{4}\/[0-9]{2}\/[0-9]{2})\/[0-9]{12}_elasticloadbalancing_.+?_(?<logfile_elb_name>[^_]+)_(?<
|
15
|
-
ACCESSLOG_REGEXP = /^((?<type>[a-z0-9]+) )?(?<time>\d{4}-\d{2}-\d{2}T\d{2}\:\d{2}\:\d{2}\.\d{6}Z) (?<elb
|
15
|
+
LOGFILE_REGEXP = /^((?<prefix>.+?)\/|)AWSLogs\/(?<account_id>[0-9]{12})\/elasticloadbalancing\/(?<region>.+?)\/(?<logfile_date>[0-9]{4}\/[0-9]{2}\/[0-9]{2})\/[0-9]{12}_elasticloadbalancing_.+?_(?<logfile_elb_name>[^_]+)_(?<logfile_timestamp>[0-9]{8}T[0-9]{4}Z)_(?<elb_ip_address>.+?)_(?<logfile_hash>.+)\.log(.gz)?$/
|
16
|
+
ACCESSLOG_REGEXP = /^((?<type>[a-z0-9]+) )?(?<time>\d{4}-\d{2}-\d{2}T\d{2}\:\d{2}\:\d{2}\.\d{6}Z) (?<elb>\S+) (?<client>\S+)\:(?<client_port>\S+) (?<target>[^:\s]+)(?::(?<target_port>\S+))? (?<request_processing_time>\S+) (?<target_processing_time>\S+) (?<response_processing_time>\S+) (?<elb_status_code>\S+) (?<target_status_code>\S+) (?<received_bytes>\S+) (?<sent_bytes>\S+) \"(?<request_method>\S+) (?<request_uri>\S+) (?<request_protocol>\S+)\" \"(?<user_agent>.+?)\" (?<ssl_cipher>\S+) (?<ssl_protocol>\S+) (?<target_group_arn>\S+) \"(?<trace_id>\S+)\" \"(?<domain_name>\S+)\" \"(?<chosen_cert_arn>\S+)\" (?<matched_rule_priority>\S+) (?<request_creation_time>\S+) \"(?<actions_executed>\S+)\" \"(?<redirect_url>\S+)\" \"(?<error_reason>\S+)\" \"(?<target_port_list>\S+)\" \"(?<target_status_code_list>\S+)\" \"(?<classification>\S+)\" \"(?<classification_reason>\S+)\" (?<conn_trace_id>\S+)/
|
16
17
|
config_param :access_key_id, :string, default: nil, secret: true
|
17
18
|
config_param :secret_access_key, :string, default: nil, secret: true
|
18
19
|
config_param :region, :string
|
@@ -28,6 +29,8 @@ class Fluent::Plugin::Elb_LogInput < Fluent::Plugin::Input
|
|
28
29
|
config_param :num_nodes, :integer, default: 1
|
29
30
|
config_param :node_no, :integer, default: 0
|
30
31
|
config_param :include_all_message, :bool, default: false
|
32
|
+
config_param :exclude_pattern_logfile_elb_name, :string, default: nil
|
33
|
+
config_param :use_sqs, :bool, default: true
|
31
34
|
|
32
35
|
def configure(conf)
|
33
36
|
super
|
@@ -38,9 +41,16 @@ class Fluent::Plugin::Elb_LogInput < Fluent::Plugin::Input
|
|
38
41
|
end
|
39
42
|
raise Fluent::ConfigError.new("s3_bucketname is required") unless @s3_bucketname
|
40
43
|
raise Fluent::ConfigError.new("timestamp_file is required") unless @timestamp_file
|
44
|
+
|
45
|
+
@s3_client = s3_client
|
41
46
|
raise Fluent::ConfigError.new("s3 bucket not found #{@s3_bucketname}") unless s3bucket_is_ok?
|
42
47
|
end
|
43
48
|
|
49
|
+
def initialize
|
50
|
+
super
|
51
|
+
@running = true
|
52
|
+
end
|
53
|
+
|
44
54
|
def start
|
45
55
|
super
|
46
56
|
|
@@ -48,11 +58,62 @@ class Fluent::Plugin::Elb_LogInput < Fluent::Plugin::Input
|
|
48
58
|
File.open(@timestamp_file, File::RDWR|File::CREAT).close
|
49
59
|
File.open(@buf_file, File::RDWR|File::CREAT).close
|
50
60
|
|
51
|
-
|
61
|
+
@exclude_pattern_logfile_elb_name_re = Regexp.new(@exclude_pattern_logfile_elb_name)
|
62
|
+
|
63
|
+
Signal.trap('INT') { shutdown }
|
64
|
+
|
65
|
+
if @use_sqs
|
66
|
+
input
|
67
|
+
@sqs_client = sqs_client
|
68
|
+
setup_sqs_timer
|
69
|
+
else
|
70
|
+
setup_input_timer
|
71
|
+
end
|
52
72
|
end
|
53
73
|
|
54
74
|
private
|
55
75
|
|
76
|
+
def shutdown
|
77
|
+
log.debug "shutdown"
|
78
|
+
if @running
|
79
|
+
@running = false
|
80
|
+
if @use_sqs
|
81
|
+
log.debug "pausing shutdown for 2x#{@refresh_interval}"
|
82
|
+
sleep 2*@refresh_interval
|
83
|
+
unset_sqs if @queue_url && @queue_url != ""
|
84
|
+
else
|
85
|
+
log.debug "pausing shutdown for 30 seconds"
|
86
|
+
sleep 30
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def setup_sqs_timer
|
92
|
+
if @running
|
93
|
+
setup_sqs
|
94
|
+
timer_execute(:in_elb_log, @refresh_interval) do
|
95
|
+
if @running
|
96
|
+
process_sqs
|
97
|
+
log.debug "sleeping for #{@refresh_interval}"
|
98
|
+
else
|
99
|
+
log.debug "Unsetting SQS"
|
100
|
+
unset_sqs if @queue_url && @queue_url != ""
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def setup_input_timer
|
107
|
+
if @running
|
108
|
+
timer_execute(:in_elb_log, @refresh_interval) do
|
109
|
+
if @running
|
110
|
+
input
|
111
|
+
log.debug "sleeping input for #{@refresh_interval}"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
56
117
|
def has_iam_role?
|
57
118
|
begin
|
58
119
|
ec2 = Aws::EC2::Client.new(region: @region)
|
@@ -62,6 +123,99 @@ class Fluent::Plugin::Elb_LogInput < Fluent::Plugin::Input
|
|
62
123
|
end
|
63
124
|
end
|
64
125
|
|
126
|
+
def setup_sqs
|
127
|
+
begin
|
128
|
+
timestamp = Time.now.to_i
|
129
|
+
queue_name = "fluent-plugin-elb-log-#{timestamp}"
|
130
|
+
|
131
|
+
sts_client = Aws::STS::Client.new
|
132
|
+
account_id = sts_client.get_caller_identity.account
|
133
|
+
|
134
|
+
queue_policy = {
|
135
|
+
"Version": "2012-10-17",
|
136
|
+
"Id": "__default_policy_ID",
|
137
|
+
"Statement": [
|
138
|
+
{
|
139
|
+
"Sid": "S3_service_publish",
|
140
|
+
"Effect": "Allow",
|
141
|
+
"Principal": {
|
142
|
+
"Service": "s3.amazonaws.com"
|
143
|
+
},
|
144
|
+
"Action": "SQS:SendMessage",
|
145
|
+
"Resource": "arn:aws:sqs:us-east-1:#{account_id}:#{queue_name}",
|
146
|
+
"Condition": {
|
147
|
+
"StringEquals": {
|
148
|
+
"aws:SourceAccount": "#{account_id}"
|
149
|
+
},
|
150
|
+
"ArnLike": {
|
151
|
+
"aws:SourceArn": "arn:aws:s3:*:*:#{@s3_bucket}"
|
152
|
+
}
|
153
|
+
}
|
154
|
+
}
|
155
|
+
]
|
156
|
+
}.to_json
|
157
|
+
create_queue_response = @sqs_client.create_queue(queue_name: queue_name,
|
158
|
+
attributes: {
|
159
|
+
'Policy' => queue_policy
|
160
|
+
}
|
161
|
+
)
|
162
|
+
@queue_url = create_queue_response.queue_url
|
163
|
+
queue_attributes = @sqs_client.get_queue_attributes(
|
164
|
+
queue_url: @queue_url,
|
165
|
+
attribute_names: ['QueueArn']
|
166
|
+
)
|
167
|
+
queue_arn = queue_attributes.attributes['QueueArn']
|
168
|
+
log.debug "New SQS queue created: #{queue_arn}"
|
169
|
+
|
170
|
+
notification_configuration = {
|
171
|
+
queue_configurations: [
|
172
|
+
{
|
173
|
+
id: "fluent-plugin-elb-log",
|
174
|
+
events: ['s3:ObjectCreated:*'],
|
175
|
+
queue_arn: "#{queue_arn}",
|
176
|
+
filter: {
|
177
|
+
key: {
|
178
|
+
filter_rules: [
|
179
|
+
{
|
180
|
+
name: 'Prefix',
|
181
|
+
value: "#{@s3_prefix}"
|
182
|
+
}
|
183
|
+
]
|
184
|
+
}
|
185
|
+
}
|
186
|
+
}
|
187
|
+
]
|
188
|
+
}
|
189
|
+
|
190
|
+
@s3_client.put_bucket_notification_configuration(
|
191
|
+
bucket: @s3_bucketname,
|
192
|
+
notification_configuration: notification_configuration
|
193
|
+
)
|
194
|
+
log.debug "S3 notification events has been set for #{@s3_bucketname}/#{@s3_prefix}"
|
195
|
+
|
196
|
+
rescue Aws::SQS::Errors::InvalidAttributeValue => e
|
197
|
+
log.debug "SQS error: #{e.message}"
|
198
|
+
|
199
|
+
rescue Aws::S3::Errors::InvalidArgument => e
|
200
|
+
log.debug "S3 Event error: #{e.message}"
|
201
|
+
sqs_client.delete_queue(queue_url: @queue_url)
|
202
|
+
log.debug "#{queue_arn} deleted"
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def unset_sqs
|
207
|
+
return if @queue_url.nil? || @queue_url == ""
|
208
|
+
@s3_client.put_bucket_notification_configuration(
|
209
|
+
bucket: @s3_bucketname,
|
210
|
+
notification_configuration: {}
|
211
|
+
)
|
212
|
+
log.debug "S3 notification events has been removed"
|
213
|
+
|
214
|
+
@sqs_client.delete_queue(queue_url: @queue_url)
|
215
|
+
log.debug "SQS queue #{@queue_url} has been removed"
|
216
|
+
@queue_url = ""
|
217
|
+
end
|
218
|
+
|
65
219
|
def get_timestamp_file
|
66
220
|
begin
|
67
221
|
# get timestamp last proc
|
@@ -125,15 +279,34 @@ class Fluent::Plugin::Elb_LogInput < Fluent::Plugin::Input
|
|
125
279
|
end
|
126
280
|
end
|
127
281
|
|
282
|
+
def sqs_client
|
283
|
+
begin
|
284
|
+
options = {
|
285
|
+
region: @region,
|
286
|
+
}
|
287
|
+
if @access_key_id && @secret_access_key
|
288
|
+
options[:access_key_id] = @access_key_id
|
289
|
+
options[:secret_access_key] = @secret_access_key
|
290
|
+
end
|
291
|
+
if @http_proxy
|
292
|
+
options[:http_proxy] = @http_proxy
|
293
|
+
end
|
294
|
+
log.debug "SQS client connect"
|
295
|
+
Aws::SQS::Client.new(options)
|
296
|
+
rescue => e
|
297
|
+
log.warn "SQS Client error occurred: #{e.message}"
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
128
301
|
def input
|
129
302
|
begin
|
130
|
-
log.debug "start"
|
303
|
+
log.debug "input start"
|
131
304
|
timestamp = get_timestamp_file()
|
132
305
|
|
133
306
|
object_keys = get_object_keys(timestamp)
|
134
307
|
object_keys = sort_object_key(object_keys)
|
135
308
|
|
136
|
-
log.info "
|
309
|
+
log.info "found #{object_keys.count} new object(s)."
|
137
310
|
|
138
311
|
object_keys.each do |object_key|
|
139
312
|
record_common = {
|
@@ -143,10 +316,10 @@ class Fluent::Plugin::Elb_LogInput < Fluent::Plugin::Input
|
|
143
316
|
"logfile_elb_name" => object_key[:logfile_elb_name],
|
144
317
|
"elb_ip_address" => object_key[:elb_ip_address],
|
145
318
|
"logfile_hash" => object_key[:logfile_hash],
|
146
|
-
"
|
319
|
+
"logfile_timestamp" => object_key[:logfile_timestamp],
|
147
320
|
"key" => object_key[:key],
|
148
321
|
"prefix" => object_key[:prefix],
|
149
|
-
"
|
322
|
+
"logfile_timestamp_unixtime" => object_key[:logfile_timestamp_unixtime],
|
150
323
|
"s3_last_modified_unixtime" => object_key[:s3_last_modified_unixtime],
|
151
324
|
}
|
152
325
|
|
@@ -164,6 +337,89 @@ class Fluent::Plugin::Elb_LogInput < Fluent::Plugin::Input
|
|
164
337
|
end
|
165
338
|
end
|
166
339
|
|
340
|
+
def process_sqs
|
341
|
+
begin
|
342
|
+
if @running
|
343
|
+
number_of_messages = @sqs_client.get_queue_attributes(
|
344
|
+
queue_url: @queue_url,
|
345
|
+
attribute_names: ["ApproximateNumberOfMessages"]
|
346
|
+
).attributes["ApproximateNumberOfMessages"].to_i
|
347
|
+
|
348
|
+
count = 0
|
349
|
+
while count < number_of_messages
|
350
|
+
messages = @sqs_client.receive_message(
|
351
|
+
queue_url: @queue_url,
|
352
|
+
max_number_of_messages: 10,
|
353
|
+
wait_time_seconds: 10
|
354
|
+
).messages
|
355
|
+
log.debug "SQS queue has ApproximateNumberOfMessages=#{number_of_messages}" if number_of_messages > 0
|
356
|
+
|
357
|
+
break if messages.empty?
|
358
|
+
|
359
|
+
messages.each do |message|
|
360
|
+
count += 1
|
361
|
+
s3_event = JSON.parse(message.body)
|
362
|
+
unless s3_event.key?('Records')
|
363
|
+
@sqs_client.delete_message(
|
364
|
+
queue_url: @queue_url,
|
365
|
+
receipt_handle: message.receipt_handle
|
366
|
+
)
|
367
|
+
next
|
368
|
+
end
|
369
|
+
#log.debug "S3 event: #{s3_event}"
|
370
|
+
bucket = s3_event['Records'][0]['s3']['bucket']['name']
|
371
|
+
key = s3_event['Records'][0]['s3']['object']['key']
|
372
|
+
event_time = Time.parse(s3_event['Records'][0]['eventTime']).to_i
|
373
|
+
log.debug "S3 event received for #{key} at #{s3_event['Records'][0]['eventTime']}"
|
374
|
+
|
375
|
+
object_key = get_object_key(key, event_time, 0)
|
376
|
+
if object_key.nil?
|
377
|
+
@sqs_client.delete_message(
|
378
|
+
queue_url: @queue_url,
|
379
|
+
receipt_handle: message.receipt_handle
|
380
|
+
)
|
381
|
+
next
|
382
|
+
end
|
383
|
+
|
384
|
+
record_common = {
|
385
|
+
"account_id" => object_key[:account_id],
|
386
|
+
"region" => object_key[:region],
|
387
|
+
"logfile_date" => object_key[:logfile_date],
|
388
|
+
"logfile_elb_name" => object_key[:logfile_elb_name],
|
389
|
+
"elb_ip_address" => object_key[:elb_ip_address],
|
390
|
+
"logfile_hash" => object_key[:logfile_hash],
|
391
|
+
"logfile_timestamp" => object_key[:logfile_timestamp],
|
392
|
+
"key" => object_key[:key],
|
393
|
+
"prefix" => object_key[:prefix],
|
394
|
+
"logfile_timestamp_unixtime" => object_key[:logfile_timestamp_unixtime],
|
395
|
+
"s3_last_modified_unixtime" => object_key[:s3_last_modified_unixtime],
|
396
|
+
}
|
397
|
+
|
398
|
+
get_file_from_s3(object_key[:key])
|
399
|
+
emit_lines_from_buffer_file(record_common)
|
400
|
+
|
401
|
+
put_timestamp_file(object_key[:s3_last_modified_unixtime])
|
402
|
+
|
403
|
+
if @delete
|
404
|
+
delete_file_from_s3(object_key[:key])
|
405
|
+
end
|
406
|
+
|
407
|
+
@sqs_client.delete_message(
|
408
|
+
queue_url: @queue_url,
|
409
|
+
receipt_handle: message.receipt_handle
|
410
|
+
)
|
411
|
+
end
|
412
|
+
if count == 0
|
413
|
+
log.debug "No messages out of #{number_of_messages} were processed - should never happen"
|
414
|
+
break
|
415
|
+
end
|
416
|
+
end
|
417
|
+
end
|
418
|
+
rescue => e
|
419
|
+
log.warn "error occurred: #{e.message}"
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
167
423
|
def sort_object_key(src_object_keys)
|
168
424
|
begin
|
169
425
|
src_object_keys.sort do |a, b|
|
@@ -175,52 +431,66 @@ class Fluent::Plugin::Elb_LogInput < Fluent::Plugin::Input
|
|
175
431
|
end
|
176
432
|
|
177
433
|
def get_object_list(max_num)
|
178
|
-
s3_client.list_objects(
|
434
|
+
@s3_client.list_objects(
|
179
435
|
bucket: @s3_bucketname,
|
180
436
|
max_keys: max_num,
|
181
437
|
prefix: @s3_prefix
|
182
438
|
)
|
183
439
|
end
|
184
440
|
|
441
|
+
def get_object_key(key, last_modified, timestamp)
|
442
|
+
node_no = Digest::SHA1.hexdigest(key).to_i(16) % @num_nodes
|
443
|
+
return nil unless node_no == @node_no
|
444
|
+
|
445
|
+
matches = LOGFILE_REGEXP.match(key)
|
446
|
+
|
447
|
+
s3_last_modified_unixtime = last_modified
|
448
|
+
if s3_last_modified_unixtime > timestamp and matches
|
449
|
+
exclude_pattern_matches = @exclude_pattern_logfile_elb_name_re.match(matches[:logfile_elb_name])
|
450
|
+
if exclude_pattern_matches
|
451
|
+
log.debug "Skipping object #{key} b/c it matches exclude_pattern_logfile_elb_name"
|
452
|
+
return nil
|
453
|
+
end
|
454
|
+
|
455
|
+
object_key = {
|
456
|
+
key: key,
|
457
|
+
prefix: matches[:prefix],
|
458
|
+
account_id: matches[:account_id],
|
459
|
+
region: matches[:region],
|
460
|
+
logfile_date: matches[:logfile_date],
|
461
|
+
logfile_elb_name: matches[:logfile_elb_name],
|
462
|
+
logfile_timestamp: matches[:logfile_timestamp],
|
463
|
+
elb_ip_address: matches[:elb_ip_address],
|
464
|
+
logfile_hash: matches[:logfile_hash],
|
465
|
+
logfile_timestamp_unixtime: Time.parse(matches[:logfile_timestamp]).to_i,
|
466
|
+
s3_last_modified_unixtime: s3_last_modified_unixtime,
|
467
|
+
}
|
468
|
+
end
|
469
|
+
|
470
|
+
return object_key
|
471
|
+
end
|
472
|
+
|
185
473
|
def get_object_keys(timestamp)
|
186
474
|
object_keys = []
|
187
475
|
|
188
|
-
resp = s3_client.list_objects_v2(
|
476
|
+
resp = @s3_client.list_objects_v2(
|
189
477
|
bucket: @s3_bucketname,
|
190
478
|
prefix: @s3_prefix
|
191
479
|
)
|
192
480
|
|
193
|
-
|
481
|
+
while @running do
|
194
482
|
resp.contents.each do |content|
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
next unless node_no == @node_no
|
200
|
-
|
201
|
-
matches = LOGFILE_REGEXP.match(object_key)
|
202
|
-
if s3_last_modified_unixtime > timestamp and matches
|
203
|
-
object_keys << {
|
204
|
-
key: object_key,
|
205
|
-
prefix: matches[:prefix],
|
206
|
-
account_id: matches[:account_id],
|
207
|
-
region: matches[:region],
|
208
|
-
logfile_date: matches[:logfile_date],
|
209
|
-
logfile_elb_name: matches[:logfile_elb_name],
|
210
|
-
elb_timestamp: matches[:elb_timestamp],
|
211
|
-
elb_ip_address: matches[:elb_ip_address],
|
212
|
-
logfile_hash: matches[:logfile_hash],
|
213
|
-
elb_timestamp_unixtime: Time.parse(matches[:elb_timestamp]).to_i,
|
214
|
-
s3_last_modified_unixtime: s3_last_modified_unixtime,
|
215
|
-
}
|
216
|
-
end
|
483
|
+
log.debug "Getting #{content.key}"
|
484
|
+
object_key = get_object_key(content.key, content.last_modified.to_i, timestamp)
|
485
|
+
next if object_key.nil?
|
486
|
+
object_keys << object_key
|
217
487
|
end
|
218
488
|
|
219
489
|
if !resp.is_truncated
|
220
490
|
return object_keys
|
221
491
|
end
|
222
492
|
|
223
|
-
resp = s3_client.list_objects_v2(
|
493
|
+
resp = @s3_client.list_objects_v2(
|
224
494
|
bucket: @s3_bucketname,
|
225
495
|
prefix: @s3_prefix,
|
226
496
|
continuation_token: resp.next_continuation_token
|
@@ -248,10 +518,10 @@ class Fluent::Plugin::Elb_LogInput < Fluent::Plugin::Input
|
|
248
518
|
|
249
519
|
def get_file_from_s3(object_name)
|
250
520
|
begin
|
251
|
-
log.debug "
|
521
|
+
log.debug "retrieving #{object_name}"
|
252
522
|
|
253
523
|
Tempfile.create('fluent-elblog') do |tfile|
|
254
|
-
s3_client.get_object(bucket: @s3_bucketname, key: object_name, response_target: tfile.path)
|
524
|
+
@s3_client.get_object(bucket: @s3_bucketname, key: object_name, response_target: tfile.path)
|
255
525
|
|
256
526
|
if File.extname(object_name) != '.gz'
|
257
527
|
FileUtils.cp(tfile.path, @buf_file)
|
@@ -268,7 +538,7 @@ class Fluent::Plugin::Elb_LogInput < Fluent::Plugin::Input
|
|
268
538
|
begin
|
269
539
|
log.debug "deleting object from s3 name is #{object_name}"
|
270
540
|
|
271
|
-
s3_client.delete_object(bucket: @s3_bucketname, key: object_name)
|
541
|
+
@s3_client.delete_object(bucket: @s3_bucketname, key: object_name)
|
272
542
|
rescue => e
|
273
543
|
log.warn "error occurred: #{e.message}, #{e.backtrace}"
|
274
544
|
end
|
@@ -308,13 +578,13 @@ class Fluent::Plugin::Elb_LogInput < Fluent::Plugin::Input
|
|
308
578
|
"elb" => item[:elb],
|
309
579
|
"client" => item[:client],
|
310
580
|
"client_port" => item[:client_port],
|
311
|
-
"
|
312
|
-
"
|
581
|
+
"target" => item[:target],
|
582
|
+
"target_port" => item[:target_port],
|
313
583
|
"request_processing_time" => item[:request_processing_time].to_f,
|
314
|
-
"
|
584
|
+
"target_processing_time" => item[:target_processing_time].to_f,
|
315
585
|
"response_processing_time" => item[:response_processing_time].to_f,
|
316
586
|
"elb_status_code" => item[:elb_status_code],
|
317
|
-
"
|
587
|
+
"target_status_code" => item[:target_status_code],
|
318
588
|
"received_bytes" => item[:received_bytes].to_i,
|
319
589
|
"sent_bytes" => item[:sent_bytes].to_i,
|
320
590
|
"request_method" => item[:request_method],
|
@@ -333,9 +603,11 @@ class Fluent::Plugin::Elb_LogInput < Fluent::Plugin::Input
|
|
333
603
|
"actions_executed" => item[:actions_executed],
|
334
604
|
"redirect_url" => item[:redirect_url],
|
335
605
|
"error_reason" => item[:error_reason],
|
336
|
-
"
|
337
|
-
"
|
338
|
-
"
|
606
|
+
"target_port_list" => item[:target_port_list],
|
607
|
+
"target_status_code_list" => item[:target_status_code_list],
|
608
|
+
"classification" => item[:classification],
|
609
|
+
"classification_reason" => item[:classification_reason],
|
610
|
+
"conn_trace_id" => item[:conn_trace_id]
|
339
611
|
}
|
340
612
|
end
|
341
613
|
end
|
data/test/plugin/in_elb_log.rb
CHANGED
@@ -164,7 +164,7 @@ class Elb_LogInputTest < Test::Unit::TestCase
|
|
164
164
|
assert_equal('ap-northeast-1', m[:region])
|
165
165
|
assert_equal('2017/05/03', m[:logfile_date])
|
166
166
|
assert_equal('app.elbname.59bfa19e900030c2', m[:logfile_elb_name])
|
167
|
-
assert_equal('20170503T1310Z', m[:
|
167
|
+
assert_equal('20170503T1310Z', m[:logfile_timestamp])
|
168
168
|
assert_equal('10.0.0.1', m[:elb_ip_address])
|
169
169
|
assert_equal('2tko12gv', m[:logfile_hash])
|
170
170
|
end
|
@@ -177,13 +177,13 @@ class Elb_LogInputTest < Test::Unit::TestCase
|
|
177
177
|
assert_equal('elbname', m[:elb])
|
178
178
|
assert_equal('10.11.12.13', m[:client])
|
179
179
|
assert_equal('37852', m[:client_port])
|
180
|
-
assert_equal('192.168.30.186', m[:
|
181
|
-
assert_equal('443', m[:
|
180
|
+
assert_equal('192.168.30.186', m[:target])
|
181
|
+
assert_equal('443', m[:target_port])
|
182
182
|
assert_equal('0.00004', m[:request_processing_time])
|
183
|
-
assert_equal('0.085372', m[:
|
183
|
+
assert_equal('0.085372', m[:target_processing_time])
|
184
184
|
assert_equal('0.000039', m[:response_processing_time])
|
185
185
|
assert_equal('301', m[:elb_status_code])
|
186
|
-
assert_equal('301', m[:
|
186
|
+
assert_equal('301', m[:target_status_code])
|
187
187
|
assert_equal('0', m[:received_bytes])
|
188
188
|
assert_equal('0', m[:sent_bytes])
|
189
189
|
assert_equal('GET', m[:request_method])
|
@@ -195,7 +195,7 @@ class Elb_LogInputTest < Test::Unit::TestCase
|
|
195
195
|
assert_equal(nil, m[:type])
|
196
196
|
assert_equal(nil, m[:target_group_arn])
|
197
197
|
assert_equal(nil, m[:trace_id])
|
198
|
-
assert_equal(nil, m[:
|
198
|
+
assert_equal(nil, m[:conn_trace_id])
|
199
199
|
end
|
200
200
|
|
201
201
|
def test_log_application_lb_parse
|
@@ -206,13 +206,13 @@ class Elb_LogInputTest < Test::Unit::TestCase
|
|
206
206
|
assert_equal('app/elbname/59bfa19e900030c2', m[:elb])
|
207
207
|
assert_equal('10.20.30.40', m[:client])
|
208
208
|
assert_equal('52730', m[:client_port])
|
209
|
-
assert_equal('192.168.30.186', m[:
|
210
|
-
assert_equal('443', m[:
|
209
|
+
assert_equal('192.168.30.186', m[:target])
|
210
|
+
assert_equal('443', m[:target_port])
|
211
211
|
assert_equal('0.006', m[:request_processing_time])
|
212
|
-
assert_equal('0.000', m[:
|
212
|
+
assert_equal('0.000', m[:target_processing_time])
|
213
213
|
assert_equal('0.086', m[:response_processing_time])
|
214
214
|
assert_equal('301', m[:elb_status_code])
|
215
|
-
assert_equal('301', m[:
|
215
|
+
assert_equal('301', m[:target_status_code])
|
216
216
|
assert_equal('117', m[:received_bytes])
|
217
217
|
assert_equal('507', m[:sent_bytes])
|
218
218
|
assert_equal('GET', m[:request_method])
|
@@ -224,7 +224,7 @@ class Elb_LogInputTest < Test::Unit::TestCase
|
|
224
224
|
assert_equal('https', m[:type])
|
225
225
|
assert_equal('arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/lbgrp1/605122a4e4ee9f2d', m[:target_group_arn])
|
226
226
|
assert_equal('"Root=1-590c7929-4eb4cb393d46a01d22db8473"', m[:trace_id])
|
227
|
-
assert_equal(nil, m[:
|
227
|
+
assert_equal(nil, m[:conn_trace_id])
|
228
228
|
end
|
229
229
|
|
230
230
|
def test_grp_and_trace_fileld
|
@@ -235,13 +235,13 @@ class Elb_LogInputTest < Test::Unit::TestCase
|
|
235
235
|
assert_equal('app/elbname/aabbccdd9988', m[:elb])
|
236
236
|
assert_equal('10.248.9.92', m[:client])
|
237
237
|
assert_equal('54000', m[:client_port])
|
238
|
-
assert_equal('10.248.9.77', m[:
|
239
|
-
assert_equal('80', m[:
|
238
|
+
assert_equal('10.248.9.77', m[:target])
|
239
|
+
assert_equal('80', m[:target_port])
|
240
240
|
assert_equal('0.001', m[:request_processing_time])
|
241
|
-
assert_equal('0.002', m[:
|
241
|
+
assert_equal('0.002', m[:target_processing_time])
|
242
242
|
assert_equal('0.003', m[:response_processing_time])
|
243
243
|
assert_equal('200', m[:elb_status_code])
|
244
|
-
assert_equal('200', m[:
|
244
|
+
assert_equal('200', m[:target_status_code])
|
245
245
|
assert_equal('686', m[:received_bytes])
|
246
246
|
assert_equal('6476', m[:sent_bytes])
|
247
247
|
assert_equal('GET', m[:request_method])
|
@@ -252,7 +252,9 @@ class Elb_LogInputTest < Test::Unit::TestCase
|
|
252
252
|
assert_equal('-', m[:ssl_protocol])
|
253
253
|
assert_equal('arn:aws:elasticloadbalancing:us-east-1:123456789123:targetgroup/example-service/1234abcd1234abcd', m[:target_group_arn])
|
254
254
|
assert_equal('"Root=1-xxxxxxxx-yyyyyyyyyyyyyyyyyyyzzzzz"', m[:trace_id])
|
255
|
-
assert_equal('
|
255
|
+
assert_equal('domain_name', m[:domain_name])
|
256
|
+
assert_equal('chosen_cert_arn', m[:chosen_cert_arn])
|
257
|
+
assert_equal('matched_rule_priority', m[:matched_rule_priority])
|
256
258
|
end
|
257
259
|
|
258
260
|
def test_alb_all_field
|
@@ -264,14 +266,14 @@ class Elb_LogInputTest < Test::Unit::TestCase
|
|
264
266
|
assert_equal('app/my-alb/520e61ffffffffff', m[:elb])
|
265
267
|
assert_equal('60.11.22.33', m[:client])
|
266
268
|
assert_equal('51306', m[:client_port])
|
267
|
-
assert_equal('192.168.30.111', m[:
|
268
|
-
assert_equal('443', m[:
|
269
|
+
assert_equal('192.168.30.111', m[:target])
|
270
|
+
assert_equal('443', m[:target_port])
|
269
271
|
|
270
272
|
assert_equal('0.010', m[:request_processing_time])
|
271
|
-
assert_equal('0.097', m[:
|
273
|
+
assert_equal('0.097', m[:target_processing_time])
|
272
274
|
assert_equal('0.001', m[:response_processing_time])
|
273
275
|
assert_equal('301', m[:elb_status_code])
|
274
|
-
assert_equal('301', m[:
|
276
|
+
assert_equal('301', m[:target_status_code])
|
275
277
|
assert_equal('414', m[:received_bytes])
|
276
278
|
assert_equal('507', m[:sent_bytes])
|
277
279
|
assert_equal('GET', m[:request_method])
|
@@ -289,8 +291,8 @@ class Elb_LogInputTest < Test::Unit::TestCase
|
|
289
291
|
assert_equal('forward', m[:actions_executed])
|
290
292
|
assert_equal('redirect://url-something.com/', m[:redirect_url])
|
291
293
|
assert_equal('error_reason', m[:error_reason])
|
292
|
-
assert_equal('"192.168.30.186:443"', m[:
|
293
|
-
assert_equal('"301"', m[:
|
294
|
-
assert_equal(nil, m[:
|
294
|
+
assert_equal('"192.168.30.186:443"', m[:target_port_list])
|
295
|
+
assert_equal('"301"', m[:target_status_code_list])
|
296
|
+
assert_equal(nil, m[:classification])
|
295
297
|
end
|
296
298
|
end
|
metadata
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fluent-plugin-elb-log
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- shinsaka
|
8
|
+
- jazzl0ver
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
|
-
date:
|
12
|
+
date: 2025-03-01 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
14
|
- !ruby/object:Gem::Dependency
|
14
15
|
name: fluentd
|
@@ -146,7 +147,7 @@ files:
|
|
146
147
|
- lib/fluent/plugin/in_elb_log.rb
|
147
148
|
- test/helper.rb
|
148
149
|
- test/plugin/in_elb_log.rb
|
149
|
-
homepage: https://github.com/
|
150
|
+
homepage: https://github.com/jazzl0ver/fluent-plugin-elb-log
|
150
151
|
licenses:
|
151
152
|
- MIT
|
152
153
|
metadata: {}
|
@@ -165,7 +166,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
165
166
|
- !ruby/object:Gem::Version
|
166
167
|
version: '0'
|
167
168
|
requirements: []
|
168
|
-
rubygems_version: 3.
|
169
|
+
rubygems_version: 3.3.5
|
169
170
|
signing_key:
|
170
171
|
specification_version: 4
|
171
172
|
summary: Amazon ELB log input plugin
|