fluent-plugin-cloudwatch-logs 0.7.3 → 0.7.4
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/.travis.yml +8 -0
- data/README.md +1 -0
- data/Rakefile +1 -0
- data/fluent-plugin-cloudwatch-logs.gemspec +2 -1
- data/lib/fluent/plugin/cloudwatch/logs/version.rb +1 -1
- data/lib/fluent/plugin/in_cloudwatch_logs.rb +16 -12
- data/test/plugin/test_in_cloudwatch_logs.rb +444 -161
- data/test/plugin/test_out_cloudwatch_logs.rb +575 -567
- data/test/test_helper.rb +2 -1
- metadata +21 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1889a17c18ac0d47b2f8d4fb6e13cebd381743275698119fbdd2cfcd6742a752
|
4
|
+
data.tar.gz: cbde319d241346c6935429887547ffa1ff45aedf3df134156a19bf825bb6523b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4827ac8cdfb0aab178aa9590c356e17a3476310383201ae1bc585d447b81143bfdc78907b2bd08615653af77c41bbc401bcb8a12bab3aa5f2c1415c25794afd8
|
7
|
+
data.tar.gz: 5a7472ba731deea05d57f7e584ee5cac64a0be2368c22d38b61be2a5b1992366182592d2c191d1fdc0c85bad58036bad51c2bb99c8d3b5c05a9362aa582b70af
|
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -154,6 +154,7 @@ Fetch sample log from CloudWatch Logs:
|
|
154
154
|
* `tag`: fluentd tag
|
155
155
|
* `use_log_stream_name_prefix`: to use `log_stream_name` as log stream name prefix (default false)
|
156
156
|
* `use_todays_log_stream`: use todays and yesterdays date as log stream name prefix (formatted YYYY/MM/DD). (default: `false`)
|
157
|
+
* `use_aws_timestamp`: get timestamp from Cloudwatch event for non json logs, otherwise fluentd will parse the log to get the timestamp (default `false`)
|
157
158
|
|
158
159
|
## Test
|
159
160
|
|
data/Rakefile
CHANGED
@@ -20,8 +20,9 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.add_dependency 'fluentd', '>= 0.14.15'
|
21
21
|
spec.add_dependency 'aws-sdk-cloudwatchlogs', '~> 1.0'
|
22
22
|
|
23
|
-
spec.add_development_dependency "bundler"
|
23
|
+
spec.add_development_dependency "bundler"
|
24
24
|
spec.add_development_dependency "rake"
|
25
25
|
spec.add_development_dependency "test-unit"
|
26
|
+
spec.add_development_dependency "test-unit-rr"
|
26
27
|
spec.add_development_dependency "mocha"
|
27
28
|
end
|
@@ -9,22 +9,23 @@ module Fluent::Plugin
|
|
9
9
|
|
10
10
|
helpers :parser, :thread, :compat_parameters
|
11
11
|
|
12
|
-
config_param :aws_key_id, :string, :
|
13
|
-
config_param :aws_sec_key, :string, :
|
12
|
+
config_param :aws_key_id, :string, default: nil, secret: true
|
13
|
+
config_param :aws_sec_key, :string, default: nil, secret: true
|
14
14
|
config_param :aws_use_sts, :bool, default: false
|
15
15
|
config_param :aws_sts_role_arn, :string, default: nil
|
16
16
|
config_param :aws_sts_session_name, :string, default: 'fluentd'
|
17
|
-
config_param :region, :string, :
|
18
|
-
config_param :endpoint, :string, :
|
17
|
+
config_param :region, :string, default: nil
|
18
|
+
config_param :endpoint, :string, default: nil
|
19
19
|
config_param :tag, :string
|
20
20
|
config_param :log_group_name, :string
|
21
|
-
config_param :log_stream_name, :string, :
|
21
|
+
config_param :log_stream_name, :string, default: nil
|
22
22
|
config_param :use_log_stream_name_prefix, :bool, default: false
|
23
23
|
config_param :state_file, :string
|
24
24
|
config_param :fetch_interval, :time, default: 60
|
25
25
|
config_param :http_proxy, :string, default: nil
|
26
|
-
config_param :json_handler, :enum, list: [:yajl, :json], :
|
26
|
+
config_param :json_handler, :enum, list: [:yajl, :json], default: :yajl
|
27
27
|
config_param :use_todays_log_stream, :bool, default: false
|
28
|
+
config_param :use_aws_timestamp, :bool, default: false
|
28
29
|
|
29
30
|
config_section :parse do
|
30
31
|
config_set_default :@type, 'none'
|
@@ -95,7 +96,7 @@ module Fluent::Plugin
|
|
95
96
|
end
|
96
97
|
|
97
98
|
def store_next_token(token, log_stream_name = nil)
|
98
|
-
open(state_file_for(log_stream_name), 'w') do |f|
|
99
|
+
File.open(state_file_for(log_stream_name), 'w') do |f|
|
99
100
|
f.write token
|
100
101
|
end
|
101
102
|
end
|
@@ -136,7 +137,10 @@ module Fluent::Plugin
|
|
136
137
|
|
137
138
|
def emit(stream, event)
|
138
139
|
if @parser
|
139
|
-
@parser.parse(event.message) {|time,
|
140
|
+
@parser.parse(event.message) {|time,record|
|
141
|
+
if @use_aws_timestamp
|
142
|
+
time = (event.timestamp / 1000).floor
|
143
|
+
end
|
140
144
|
router.emit(@tag, time, record)
|
141
145
|
}
|
142
146
|
else
|
@@ -152,7 +156,7 @@ module Fluent::Plugin
|
|
152
156
|
log_stream_name: log_stream_name
|
153
157
|
}
|
154
158
|
log_next_token = next_token(log_stream_name)
|
155
|
-
request[:next_token] = log_next_token if !log_next_token.nil? && !log_next_token.empty?
|
159
|
+
request[:next_token] = log_next_token if !log_next_token.nil? && !log_next_token.empty?
|
156
160
|
response = @logs.get_log_events(request)
|
157
161
|
if valid_next_token(log_next_token, response.next_forward_token)
|
158
162
|
store_next_token(response.next_forward_token, log_stream_name)
|
@@ -180,15 +184,15 @@ module Fluent::Plugin
|
|
180
184
|
end
|
181
185
|
|
182
186
|
def valid_next_token(prev_token, next_token)
|
183
|
-
|
187
|
+
next_token && prev_token != next_token.chomp
|
184
188
|
end
|
185
189
|
|
186
190
|
def get_todays_date
|
187
|
-
|
191
|
+
Date.today.strftime("%Y/%m/%d")
|
188
192
|
end
|
189
193
|
|
190
194
|
def get_yesterdays_date
|
191
|
-
|
195
|
+
(Date.today - 1).strftime("%Y/%m/%d")
|
192
196
|
end
|
193
197
|
end
|
194
198
|
end
|
@@ -2,6 +2,7 @@ require 'test_helper'
|
|
2
2
|
require 'fluent/test/driver/input'
|
3
3
|
require 'fluent/test/helpers'
|
4
4
|
require 'date'
|
5
|
+
require 'fluent/plugin/in_cloudwatch_logs'
|
5
6
|
|
6
7
|
class CloudwatchLogsInputTest < Test::Unit::TestCase
|
7
8
|
include CloudwatchLogsTestHelper
|
@@ -9,70 +10,121 @@ class CloudwatchLogsInputTest < Test::Unit::TestCase
|
|
9
10
|
|
10
11
|
def setup
|
11
12
|
Fluent::Test.setup
|
12
|
-
require 'fluent/plugin/in_cloudwatch_logs'
|
13
|
-
|
14
13
|
end
|
15
14
|
|
16
|
-
|
17
|
-
|
15
|
+
sub_test_case "configure" do
|
16
|
+
def test_configure
|
17
|
+
d = create_driver(<<-EOC)
|
18
|
+
@type cloudwatch_logs
|
19
|
+
aws_key_id test_id
|
20
|
+
aws_sec_key test_key
|
21
|
+
region us-east-1
|
22
|
+
tag test
|
23
|
+
log_group_name group
|
24
|
+
log_stream_name stream
|
25
|
+
use_log_stream_name_prefix true
|
26
|
+
state_file /tmp/state
|
27
|
+
use_aws_timestamp true
|
28
|
+
EOC
|
29
|
+
|
30
|
+
assert_equal('test_id', d.instance.aws_key_id)
|
31
|
+
assert_equal('test_key', d.instance.aws_sec_key)
|
32
|
+
assert_equal('us-east-1', d.instance.region)
|
33
|
+
assert_equal('test', d.instance.tag)
|
34
|
+
assert_equal('group', d.instance.log_group_name)
|
35
|
+
assert_equal('stream', d.instance.log_stream_name)
|
36
|
+
assert_equal(true, d.instance.use_log_stream_name_prefix)
|
37
|
+
assert_equal('/tmp/state', d.instance.state_file)
|
38
|
+
assert_equal(:yajl, d.instance.json_handler)
|
39
|
+
assert_equal(true, d.instance.use_aws_timestamp)
|
40
|
+
end
|
18
41
|
end
|
19
42
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
aws_sec_key test_key
|
25
|
-
region us-east-1
|
26
|
-
tag test
|
27
|
-
log_group_name group
|
28
|
-
log_stream_name stream
|
29
|
-
use_log_stream_name_prefix true
|
30
|
-
state_file /tmp/state
|
31
|
-
EOC
|
43
|
+
sub_test_case "real world" do
|
44
|
+
def setup
|
45
|
+
omit if ENV["CI"] == "true"
|
46
|
+
end
|
32
47
|
|
33
|
-
|
34
|
-
|
35
|
-
assert_equal('us-east-1', d.instance.region)
|
36
|
-
assert_equal('test', d.instance.tag)
|
37
|
-
assert_equal('group', d.instance.log_group_name)
|
38
|
-
assert_equal('stream', d.instance.log_stream_name)
|
39
|
-
assert_equal(true, d.instance.use_log_stream_name_prefix)
|
40
|
-
assert_equal('/tmp/state', d.instance.state_file)
|
41
|
-
assert_equal(:yajl, d.instance.json_handler)
|
42
|
-
end
|
48
|
+
def teardown
|
49
|
+
return if ENV["CI"] == "true"
|
43
50
|
|
44
|
-
|
45
|
-
|
51
|
+
clear_log_group
|
52
|
+
end
|
46
53
|
|
47
|
-
|
48
|
-
|
49
|
-
{timestamp: time_ms, message: '{"cloudwatch":"logs1"}'},
|
50
|
-
{timestamp: time_ms, message: '{"cloudwatch":"logs2"}'},
|
51
|
-
])
|
54
|
+
def test_emit
|
55
|
+
create_log_stream
|
52
56
|
|
53
|
-
|
57
|
+
time_ms = (Time.now.to_f * 1000).floor
|
58
|
+
put_log_events([
|
59
|
+
{timestamp: time_ms, message: '{"cloudwatch":"logs1"}'},
|
60
|
+
{timestamp: time_ms, message: '{"cloudwatch":"logs2"}'},
|
61
|
+
])
|
54
62
|
|
55
|
-
|
56
|
-
d.run(expect_emits: 2, timeout: 5)
|
63
|
+
sleep 5
|
57
64
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
65
|
+
d = create_driver
|
66
|
+
d.run(expect_emits: 2, timeout: 5)
|
67
|
+
|
68
|
+
emits = d.events
|
69
|
+
assert_equal(2, emits.size)
|
70
|
+
assert_equal(['test', (time_ms / 1000).floor, {'cloudwatch' => 'logs1'}], emits[0])
|
71
|
+
assert_equal(['test', (time_ms / 1000).floor, {'cloudwatch' => 'logs2'}], emits[1])
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_emit_with_aws_timestamp
|
75
|
+
create_log_stream
|
76
|
+
|
77
|
+
time_ms = (Time.now.to_f * 1000).floor
|
78
|
+
log_time_ms = time_ms - 10000
|
79
|
+
put_log_events([
|
80
|
+
{timestamp: time_ms, message: Time.at(log_time_ms/1000.floor).to_s + ",Cloudwatch non json logs1"},
|
81
|
+
{timestamp: time_ms, message: Time.at(log_time_ms/1000.floor).to_s + ",Cloudwatch non json logs2"},
|
82
|
+
])
|
83
|
+
|
84
|
+
sleep 5
|
85
|
+
|
86
|
+
d = create_driver(csv_format_config_aws_timestamp)
|
87
|
+
d.run(expect_emits: 2, timeout: 5)
|
88
|
+
|
89
|
+
emits = d.events
|
90
|
+
assert_equal(2, emits.size)
|
91
|
+
assert_equal(['test', (time_ms / 1000).floor, {"message"=>"Cloudwatch non json logs1"}], emits[0])
|
92
|
+
assert_equal(['test', (time_ms / 1000).floor, {"message"=>"Cloudwatch non json logs2"}], emits[1])
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_emit_with_log_timestamp
|
96
|
+
create_log_stream
|
97
|
+
|
98
|
+
time_ms = (Time.now.to_f * 1000).floor
|
99
|
+
log_time_ms = time_ms - 10000
|
100
|
+
put_log_events([
|
101
|
+
{timestamp: time_ms, message: Time.at(log_time_ms/1000.floor).to_s + ",Cloudwatch non json logs1"},
|
102
|
+
{timestamp: time_ms, message: Time.at(log_time_ms/1000.floor).to_s + ",Cloudwatch non json logs2"},
|
103
|
+
])
|
104
|
+
|
105
|
+
sleep 5
|
106
|
+
|
107
|
+
d = create_driver(csv_format_config)
|
108
|
+
d.run(expect_emits: 2, timeout: 5)
|
63
109
|
|
64
|
-
|
65
|
-
|
110
|
+
emits = d.events
|
111
|
+
assert_equal(2, emits.size)
|
112
|
+
assert_equal(['test', (log_time_ms / 1000).floor, {"message"=>"Cloudwatch non json logs1"}], emits[0])
|
113
|
+
assert_equal(['test', (log_time_ms / 1000).floor, {"message"=>"Cloudwatch non json logs2"}], emits[1])
|
114
|
+
end
|
66
115
|
|
67
|
-
|
68
|
-
|
69
|
-
{timestamp: time_ms, message: 'logs1'},
|
70
|
-
{timestamp: time_ms, message: 'logs2'},
|
71
|
-
])
|
116
|
+
def test_emit_width_format
|
117
|
+
create_log_stream
|
72
118
|
|
73
|
-
|
119
|
+
time_ms = (Time.now.to_f * 1000).floor
|
120
|
+
put_log_events([
|
121
|
+
{timestamp: time_ms, message: 'logs1'},
|
122
|
+
{timestamp: time_ms, message: 'logs2'},
|
123
|
+
])
|
74
124
|
|
75
|
-
|
125
|
+
sleep 5
|
126
|
+
|
127
|
+
d = create_driver(<<-EOC)
|
76
128
|
tag test
|
77
129
|
@type cloudwatch_logs
|
78
130
|
log_group_name #{log_group_name}
|
@@ -85,38 +137,38 @@ class CloudwatchLogsInputTest < Test::Unit::TestCase
|
|
85
137
|
#{endpoint}
|
86
138
|
EOC
|
87
139
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
140
|
+
d.run(expect_emits: 2, timeout: 5)
|
141
|
+
|
142
|
+
emits = d.events
|
143
|
+
assert_equal(2, emits.size)
|
144
|
+
assert_equal('test', emits[0][0])
|
145
|
+
assert_in_delta((time_ms / 1000).floor, emits[0][1], 10)
|
146
|
+
assert_equal({'cloudwatch' => 'logs1'}, emits[0][2])
|
147
|
+
assert_equal('test', emits[1][0])
|
148
|
+
assert_in_delta((time_ms / 1000).floor, emits[1][1], 10)
|
149
|
+
assert_equal({'cloudwatch' => 'logs2'}, emits[1][2])
|
150
|
+
end
|
151
|
+
|
152
|
+
def test_emit_with_prefix
|
153
|
+
new_log_stream("testprefix")
|
154
|
+
create_log_stream
|
155
|
+
|
156
|
+
time_ms = (Time.now.to_f * 1000).floor
|
157
|
+
put_log_events([
|
158
|
+
{timestamp: time_ms + 1000, message: '{"cloudwatch":"logs1"}'},
|
159
|
+
{timestamp: time_ms + 2000, message: '{"cloudwatch":"logs2"}'},
|
160
|
+
])
|
161
|
+
|
162
|
+
new_log_stream("testprefix")
|
163
|
+
create_log_stream
|
164
|
+
put_log_events([
|
165
|
+
{timestamp: time_ms + 3000, message: '{"cloudwatch":"logs3"}'},
|
166
|
+
{timestamp: time_ms + 4000, message: '{"cloudwatch":"logs4"}'},
|
167
|
+
])
|
168
|
+
|
169
|
+
sleep 5
|
170
|
+
|
171
|
+
d = create_driver(<<-EOC)
|
120
172
|
tag test
|
121
173
|
@type cloudwatch_logs
|
122
174
|
log_group_name #{log_group_name}
|
@@ -128,69 +180,69 @@ class CloudwatchLogsInputTest < Test::Unit::TestCase
|
|
128
180
|
#{region}
|
129
181
|
#{endpoint}
|
130
182
|
EOC
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
183
|
+
d.run(expect_emits: 4, timeout: 5)
|
184
|
+
|
185
|
+
emits = d.events
|
186
|
+
assert_equal(4, emits.size)
|
187
|
+
assert_true(emits.include? ['test', ((time_ms + 1000) / 1000).floor, {'cloudwatch' => 'logs1'}])
|
188
|
+
assert_true(emits.include? ['test', ((time_ms + 2000) / 1000).floor, {'cloudwatch' => 'logs2'}])
|
189
|
+
assert_true(emits.include? ['test', ((time_ms + 3000) / 1000).floor, {'cloudwatch' => 'logs3'}])
|
190
|
+
assert_true(emits.include? ['test', ((time_ms + 4000) / 1000).floor, {'cloudwatch' => 'logs4'}])
|
191
|
+
end
|
192
|
+
|
193
|
+
def test_emit_with_todays_log_stream
|
194
|
+
new_log_stream("testprefix")
|
195
|
+
create_log_stream
|
196
|
+
|
197
|
+
today = DateTime.now.strftime("%Y/%m/%d")
|
198
|
+
yesterday = (Date.today - 1).strftime("%Y/%m/%d")
|
199
|
+
tomorrow = (Date.today + 1).strftime("%Y/%m/%d")
|
200
|
+
|
201
|
+
|
202
|
+
time_ms = (Time.now.to_f * 1000).floor
|
203
|
+
put_log_events([
|
204
|
+
{timestamp: time_ms + 1000, message: '{"cloudwatch":"logs1"}'},
|
205
|
+
{timestamp: time_ms + 2000, message: '{"cloudwatch":"logs2"}'},
|
206
|
+
])
|
207
|
+
|
208
|
+
new_log_stream(today)
|
209
|
+
create_log_stream
|
210
|
+
put_log_events([
|
211
|
+
{timestamp: time_ms + 3000, message: '{"cloudwatch":"logs3"}'},
|
212
|
+
{timestamp: time_ms + 4000, message: '{"cloudwatch":"logs4"}'},
|
213
|
+
])
|
214
|
+
|
215
|
+
new_log_stream(yesterday)
|
216
|
+
create_log_stream
|
217
|
+
put_log_events([
|
218
|
+
{timestamp: time_ms + 5000, message: '{"cloudwatch":"logs5"}'},
|
219
|
+
{timestamp: time_ms + 6000, message: '{"cloudwatch":"logs6"}'},
|
220
|
+
])
|
221
|
+
|
222
|
+
new_log_stream(tomorrow)
|
223
|
+
create_log_stream
|
224
|
+
put_log_events([
|
225
|
+
{timestamp: time_ms + 7000, message: '{"cloudwatch":"logs7"}'},
|
226
|
+
{timestamp: time_ms + 8000, message: '{"cloudwatch":"logs8"}'},
|
227
|
+
])
|
228
|
+
|
229
|
+
new_log_stream(today)
|
230
|
+
create_log_stream
|
231
|
+
put_log_events([
|
232
|
+
{timestamp: time_ms + 9000, message: '{"cloudwatch":"logs9"}'},
|
233
|
+
{timestamp: time_ms + 10000, message: '{"cloudwatch":"logs10"}'},
|
234
|
+
])
|
235
|
+
|
236
|
+
new_log_stream(yesterday)
|
237
|
+
create_log_stream
|
238
|
+
put_log_events([
|
239
|
+
{timestamp: time_ms + 11000, message: '{"cloudwatch":"logs11"}'},
|
240
|
+
{timestamp: time_ms + 12000, message: '{"cloudwatch":"logs12"}'},
|
241
|
+
])
|
242
|
+
|
243
|
+
sleep 15
|
244
|
+
|
245
|
+
d = create_driver(<<-EOC)
|
194
246
|
tag test
|
195
247
|
@type cloudwatch_logs
|
196
248
|
log_group_name #{log_group_name}
|
@@ -201,25 +253,234 @@ class CloudwatchLogsInputTest < Test::Unit::TestCase
|
|
201
253
|
#{region}
|
202
254
|
#{endpoint}
|
203
255
|
EOC
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
256
|
+
d.run(expect_emits: 8, timeout: 15)
|
257
|
+
|
258
|
+
emits = d.events
|
259
|
+
assert_equal(8, emits.size)
|
260
|
+
assert_false(emits.include? ['test', ((time_ms + 1000) / 1000).floor, {'cloudwatch' => 'logs1'}])
|
261
|
+
assert_false(emits.include? ['test', ((time_ms + 2000) / 1000).floor, {'cloudwatch' => 'logs2'}])
|
262
|
+
assert_true(emits.include? ['test', ((time_ms + 3000) / 1000).floor, {'cloudwatch' => 'logs3'}])
|
263
|
+
assert_true(emits.include? ['test', ((time_ms + 4000) / 1000).floor, {'cloudwatch' => 'logs4'}])
|
264
|
+
assert_true(emits.include? ['test', ((time_ms + 5000) / 1000).floor, {'cloudwatch' => 'logs5'}])
|
265
|
+
assert_true(emits.include? ['test', ((time_ms + 6000) / 1000).floor, {'cloudwatch' => 'logs6'}])
|
266
|
+
assert_false(emits.include? ['test', ((time_ms + 7000) / 1000).floor, {'cloudwatch' => 'logs7'}])
|
267
|
+
assert_false(emits.include? ['test', ((time_ms + 8000) / 1000).floor, {'cloudwatch' => 'logs8'}])
|
268
|
+
assert_true(emits.include? ['test', ((time_ms + 9000) / 1000).floor, {'cloudwatch' => 'logs9'}])
|
269
|
+
assert_true(emits.include? ['test', ((time_ms + 10000) / 1000).floor, {'cloudwatch' => 'logs10'}])
|
270
|
+
assert_true(emits.include? ['test', ((time_ms + 11000) / 1000).floor, {'cloudwatch' => 'logs11'}])
|
271
|
+
assert_true(emits.include? ['test', ((time_ms + 12000) / 1000).floor, {'cloudwatch' => 'logs12'}])
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
sub_test_case "stub responses" do
|
276
|
+
setup do
|
277
|
+
@client = Aws::CloudWatchLogs::Client.new(stub_responses: true)
|
278
|
+
mock(Aws::CloudWatchLogs::Client).new(anything) do
|
279
|
+
@client
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
test "emit" do
|
284
|
+
time_ms = (Time.now.to_f * 1000).floor
|
285
|
+
log_stream = Aws::CloudWatchLogs::Types::LogStream.new(log_stream_name: "stream_name")
|
286
|
+
@client.stub_responses(:describe_log_streams, { log_streams: [log_stream], next_token: nil })
|
287
|
+
cloudwatch_logs_events = [
|
288
|
+
Aws::CloudWatchLogs::Types::OutputLogEvent.new(timestamp: time_ms, message: { cloudwatch: "logs1" }.to_json, ingestion_time: time_ms),
|
289
|
+
Aws::CloudWatchLogs::Types::OutputLogEvent.new(timestamp: time_ms, message: { cloudwatch: "logs2" }.to_json, ingestion_time: time_ms)
|
290
|
+
]
|
291
|
+
@client.stub_responses(:get_log_events, { events: cloudwatch_logs_events, next_forward_token: nil })
|
292
|
+
|
293
|
+
d = create_driver
|
294
|
+
d.run(expect_emits: 2, timeout: 5)
|
295
|
+
|
296
|
+
events = d.events
|
297
|
+
assert_equal(2, events.size)
|
298
|
+
assert_equal(["test", (time_ms / 1000), { "cloudwatch" => "logs1" }], events[0])
|
299
|
+
assert_equal(["test", (time_ms / 1000), { "cloudwatch" => "logs2" }], events[1])
|
300
|
+
end
|
301
|
+
|
302
|
+
test "emit with aws_timestamp" do
|
303
|
+
time_ms = (Time.now.to_f * 1000).floor
|
304
|
+
log_time_ms = time_ms - 10000
|
305
|
+
log_time_str = Time.at(log_time_ms / 1000.floor).to_s
|
306
|
+
log_stream = Aws::CloudWatchLogs::Types::LogStream.new(log_stream_name: "stream_name")
|
307
|
+
@client.stub_responses(:describe_log_streams, { log_streams: [log_stream], next_token: nil })
|
308
|
+
cloudwatch_logs_events = [
|
309
|
+
Aws::CloudWatchLogs::Types::OutputLogEvent.new(timestamp: time_ms, message: "#{log_time_str},Cloudwatch non json logs1"),
|
310
|
+
Aws::CloudWatchLogs::Types::OutputLogEvent.new(timestamp: time_ms, message: "#{log_time_str},Cloudwatch non json logs2")
|
311
|
+
]
|
312
|
+
@client.stub_responses(:get_log_events, { events: cloudwatch_logs_events, next_forward_token: nil })
|
313
|
+
|
314
|
+
d = create_driver(csv_format_config_aws_timestamp)
|
315
|
+
d.run(expect_emits: 2, timeout: 5)
|
316
|
+
|
317
|
+
events = d.events
|
318
|
+
assert_equal(2, events.size)
|
319
|
+
assert_equal(["test", (time_ms / 1000).floor, { "message" => "Cloudwatch non json logs1" }], events[0])
|
320
|
+
assert_equal(["test", (time_ms / 1000).floor, { "message" => "Cloudwatch non json logs2" }], events[1])
|
321
|
+
end
|
322
|
+
|
323
|
+
test "emit with log_timestamp" do
|
324
|
+
time_ms = (Time.now.to_f * 1000).floor
|
325
|
+
log_time_ms = time_ms - 10000
|
326
|
+
log_time_str = Time.at(log_time_ms / 1000.floor).to_s
|
327
|
+
log_stream = Aws::CloudWatchLogs::Types::LogStream.new(log_stream_name: "stream_name")
|
328
|
+
@client.stub_responses(:describe_log_streams, { log_streams: [log_stream], next_token: nil })
|
329
|
+
cloudwatch_logs_events = [
|
330
|
+
Aws::CloudWatchLogs::Types::OutputLogEvent.new(timestamp: time_ms, message: "#{log_time_str},Cloudwatch non json logs1"),
|
331
|
+
Aws::CloudWatchLogs::Types::OutputLogEvent.new(timestamp: time_ms, message: "#{log_time_str},Cloudwatch non json logs2")
|
332
|
+
]
|
333
|
+
@client.stub_responses(:get_log_events, { events: cloudwatch_logs_events, next_forward_token: nil })
|
334
|
+
|
335
|
+
d = create_driver(csv_format_config)
|
336
|
+
d.run(expect_emits: 2, timeout: 5)
|
337
|
+
|
338
|
+
emits = d.events
|
339
|
+
assert_equal(2, emits.size)
|
340
|
+
assert_equal(["test", (log_time_ms / 1000).floor, { "message" => "Cloudwatch non json logs1" }], emits[0])
|
341
|
+
assert_equal(["test", (log_time_ms / 1000).floor, { "message" => "Cloudwatch non json logs2" }], emits[1])
|
342
|
+
end
|
343
|
+
|
344
|
+
test "emit with format" do
|
345
|
+
config = <<-CONFIG
|
346
|
+
tag test
|
347
|
+
@type cloudwatch_logs
|
348
|
+
log_group_name #{log_group_name}
|
349
|
+
log_stream_name #{log_stream_name}
|
350
|
+
state_file /tmp/state
|
351
|
+
format /^(?<cloudwatch>[^ ]*)?/
|
352
|
+
#{aws_key_id}
|
353
|
+
#{aws_sec_key}
|
354
|
+
#{region}
|
355
|
+
#{endpoint}
|
356
|
+
CONFIG
|
357
|
+
time_ms = (Time.now.to_f * 1000).floor
|
358
|
+
|
359
|
+
log_stream = Aws::CloudWatchLogs::Types::LogStream.new(log_stream_name: "stream_name")
|
360
|
+
@client.stub_responses(:describe_log_streams, { log_streams: [log_stream], next_token: nil })
|
361
|
+
cloudwatch_logs_events = [
|
362
|
+
Aws::CloudWatchLogs::Types::OutputLogEvent.new(timestamp: time_ms, message: "logs1", ingestion_time: time_ms),
|
363
|
+
Aws::CloudWatchLogs::Types::OutputLogEvent.new(timestamp: time_ms, message: "logs2", ingestion_time: time_ms)
|
364
|
+
]
|
365
|
+
@client.stub_responses(:get_log_events, { events: cloudwatch_logs_events, next_forward_token: nil })
|
366
|
+
|
367
|
+
d = create_driver(config)
|
368
|
+
d.run(expect_emits: 2, timeout: 5)
|
369
|
+
|
370
|
+
events = d.events
|
371
|
+
assert_equal(2, events.size)
|
372
|
+
assert_equal("test", events[0][0])
|
373
|
+
assert_in_delta(time_ms / 1000.0, events[0][1], 1.0)
|
374
|
+
assert_equal({ "cloudwatch" => "logs1" }, events[0][2])
|
375
|
+
assert_equal("test", events[1][0])
|
376
|
+
assert_in_delta(time_ms / 1000.0, events[1][1], 1.0)
|
377
|
+
assert_equal({ "cloudwatch" => "logs2" }, events[1][2])
|
378
|
+
end
|
379
|
+
|
380
|
+
test "emit with prefix" do
|
381
|
+
config = <<-CONFIG
|
382
|
+
tag test
|
383
|
+
@type cloudwatch_logs
|
384
|
+
log_group_name #{log_group_name}
|
385
|
+
log_stream_name testprefix
|
386
|
+
use_log_stream_name_prefix true
|
387
|
+
state_file /tmp/state
|
388
|
+
#{aws_key_id}
|
389
|
+
#{aws_sec_key}
|
390
|
+
#{region}
|
391
|
+
#{endpoint}
|
392
|
+
CONFIG
|
393
|
+
time_ms = (Time.now.to_f * 1000).floor
|
394
|
+
log_stream1 = Aws::CloudWatchLogs::Types::LogStream.new(log_stream_name: "stream_name")
|
395
|
+
log_stream2 = Aws::CloudWatchLogs::Types::LogStream.new(log_stream_name: "stream_name")
|
396
|
+
@client.stub_responses(:describe_log_streams, { log_streams: [log_stream1, log_stream2], next_token: nil })
|
397
|
+
cloudwatch_logs_events1 = [
|
398
|
+
Aws::CloudWatchLogs::Types::OutputLogEvent.new(timestamp: time_ms + 1000, message: { cloudwatch: "logs1" }.to_json, ingestion_time: time_ms),
|
399
|
+
Aws::CloudWatchLogs::Types::OutputLogEvent.new(timestamp: time_ms + 2000, message: { cloudwatch: "logs2" }.to_json, ingestion_time: time_ms)
|
400
|
+
]
|
401
|
+
cloudwatch_logs_events2 = [
|
402
|
+
Aws::CloudWatchLogs::Types::OutputLogEvent.new(timestamp: time_ms + 3000, message: { cloudwatch: "logs3" }.to_json, ingestion_time: time_ms),
|
403
|
+
Aws::CloudWatchLogs::Types::OutputLogEvent.new(timestamp: time_ms + 4000, message: { cloudwatch: "logs4" }.to_json, ingestion_time: time_ms)
|
404
|
+
]
|
405
|
+
@client.stub_responses(:get_log_events, [
|
406
|
+
{ events: cloudwatch_logs_events1, next_forward_token: nil },
|
407
|
+
{ events: cloudwatch_logs_events2, next_forward_token: nil },
|
408
|
+
])
|
409
|
+
|
410
|
+
d = create_driver(config)
|
411
|
+
d.run(expect_emits: 4, timeout: 5)
|
412
|
+
|
413
|
+
events = d.events
|
414
|
+
assert_equal(4, events.size)
|
415
|
+
assert_equal(["test", (time_ms + 1000) / 1000, { "cloudwatch" => "logs1" }], events[0])
|
416
|
+
assert_equal(["test", (time_ms + 2000) / 1000, { "cloudwatch" => "logs2" }], events[1])
|
417
|
+
assert_equal(["test", (time_ms + 3000) / 1000, { "cloudwatch" => "logs3" }], events[2])
|
418
|
+
assert_equal(["test", (time_ms + 4000) / 1000, { "cloudwatch" => "logs4" }], events[3])
|
419
|
+
end
|
420
|
+
|
421
|
+
test "emit with today's log stream" do
|
422
|
+
config = <<-CONFIG
|
423
|
+
tag test
|
424
|
+
@type cloudwatch_logs
|
425
|
+
log_group_name #{log_group_name}
|
426
|
+
use_todays_log_stream true
|
427
|
+
state_file /tmp/state
|
428
|
+
fetch_interval 0.1
|
429
|
+
#{aws_key_id}
|
430
|
+
#{aws_sec_key}
|
431
|
+
#{region}
|
432
|
+
#{endpoint}
|
433
|
+
CONFIG
|
434
|
+
|
435
|
+
today = Date.today.strftime("%Y/%m/%d")
|
436
|
+
yesterday = (Date.today - 1).strftime("%Y/%m/%d")
|
437
|
+
time_ms = (Time.now.to_f * 1000).floor
|
438
|
+
|
439
|
+
log_stream = ->(name) { Aws::CloudWatchLogs::Types::LogStream.new(log_stream_name: "#{name}_#{SecureRandom.uuid}") }
|
440
|
+
@client.stub_responses(:describe_log_streams, ->(context) {
|
441
|
+
if context.params[:log_stream_name_prefix].start_with?(today)
|
442
|
+
{ log_streams: [log_stream.call(today)], next_token: nil }
|
443
|
+
elsif context.params[:log_stream_name_prefix].start_with?(yesterday)
|
444
|
+
{ log_streams: [log_stream.call(yesterday)], next_token: nil }
|
445
|
+
else
|
446
|
+
{ log_streams: [], next_token: nil }
|
447
|
+
end
|
448
|
+
})
|
449
|
+
count = 0
|
450
|
+
@client.stub_responses(:get_log_events, ->(context) {
|
451
|
+
n = count * 2 + 1
|
452
|
+
cloudwatch_logs_events = [
|
453
|
+
Aws::CloudWatchLogs::Types::OutputLogEvent.new(timestamp: time_ms + n * 1000, message: { cloudwatch: "logs#{n}" }.to_json, ingestion_time: time_ms),
|
454
|
+
Aws::CloudWatchLogs::Types::OutputLogEvent.new(timestamp: time_ms + (n + 1) * 1000, message: { cloudwatch: "logs#{n + 1}" }.to_json, ingestion_time: time_ms)
|
455
|
+
]
|
456
|
+
count += 1
|
457
|
+
if context.params[:log_stream_name].start_with?(today)
|
458
|
+
{ events: cloudwatch_logs_events, next_forward_token: nil }
|
459
|
+
elsif context.params[:log_stream_name].start_with?(yesterday)
|
460
|
+
{ events: cloudwatch_logs_events, next_forward_token: nil }
|
461
|
+
else
|
462
|
+
flunk("Failed log_stream_name: #{context.params[:log_stream_name]}")
|
463
|
+
end
|
464
|
+
})
|
465
|
+
|
466
|
+
d = create_driver(config)
|
467
|
+
d.run(expect_emits: 8, timeout: 15)
|
468
|
+
|
469
|
+
events = d.events
|
470
|
+
assert_equal(8, events.size)
|
471
|
+
assert_equal(["test", ((time_ms + 1000) / 1000), { "cloudwatch" => "logs1" }], events[0])
|
472
|
+
assert_equal(["test", ((time_ms + 2000) / 1000), { "cloudwatch" => "logs2" }], events[1])
|
473
|
+
assert_equal(["test", ((time_ms + 3000) / 1000), { "cloudwatch" => "logs3" }], events[2])
|
474
|
+
assert_equal(["test", ((time_ms + 4000) / 1000), { "cloudwatch" => "logs4" }], events[3])
|
475
|
+
assert_equal(["test", ((time_ms + 5000) / 1000), { "cloudwatch" => "logs5" }], events[4])
|
476
|
+
assert_equal(["test", ((time_ms + 6000) / 1000), { "cloudwatch" => "logs6" }], events[5])
|
477
|
+
assert_equal(["test", ((time_ms + 7000) / 1000), { "cloudwatch" => "logs7" }], events[6])
|
478
|
+
assert_equal(["test", ((time_ms + 8000) / 1000), { "cloudwatch" => "logs8" }], events[7])
|
479
|
+
end
|
220
480
|
end
|
221
481
|
|
222
482
|
private
|
483
|
+
|
223
484
|
def default_config
|
224
485
|
<<-EOC
|
225
486
|
tag test
|
@@ -235,6 +496,28 @@ class CloudwatchLogsInputTest < Test::Unit::TestCase
|
|
235
496
|
EOC
|
236
497
|
end
|
237
498
|
|
499
|
+
def csv_format_config
|
500
|
+
<<-EOC
|
501
|
+
tag test
|
502
|
+
@type cloudwatch_logs
|
503
|
+
log_group_name #{log_group_name}
|
504
|
+
log_stream_name #{log_stream_name}
|
505
|
+
state_file /tmp/state
|
506
|
+
fetch_interval 1
|
507
|
+
#{aws_key_id}
|
508
|
+
#{aws_sec_key}
|
509
|
+
#{region}
|
510
|
+
#{endpoint}
|
511
|
+
format csv
|
512
|
+
keys time,message
|
513
|
+
time_key time
|
514
|
+
EOC
|
515
|
+
end
|
516
|
+
|
517
|
+
def csv_format_config_aws_timestamp
|
518
|
+
csv_format_config.concat("use_aws_timestamp true")
|
519
|
+
end
|
520
|
+
|
238
521
|
def create_driver(conf = default_config)
|
239
522
|
Fluent::Test::Driver::Input.new(Fluent::Plugin::CloudwatchLogsInput).configure(conf)
|
240
523
|
end
|