rb-fluent-plugin-cloudwatch-logs 0.7.1.pre.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,241 @@
1
+ require 'test_helper'
2
+ require 'fluent/test/driver/input'
3
+ require 'fluent/test/helpers'
4
+ require 'date'
5
+
6
+ class CloudwatchLogsInputTest < Test::Unit::TestCase
7
+ include CloudwatchLogsTestHelper
8
+ include Fluent::Test::Helpers
9
+
10
+ def setup
11
+ Fluent::Test.setup
12
+ require 'fluent/plugin/in_cloudwatch_logs'
13
+
14
+ end
15
+
16
+ def teardown
17
+ clear_log_group
18
+ end
19
+
20
+ def test_configure
21
+ d = create_driver(<<-EOC)
22
+ @type cloudwatch_logs
23
+ aws_key_id test_id
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
32
+
33
+ assert_equal('test_id', d.instance.aws_key_id)
34
+ assert_equal('test_key', d.instance.aws_sec_key)
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
43
+
44
+ def test_emit
45
+ create_log_stream
46
+
47
+ time_ms = (Time.now.to_f * 1000).floor
48
+ put_log_events([
49
+ {timestamp: time_ms, message: '{"cloudwatch":"logs1"}'},
50
+ {timestamp: time_ms, message: '{"cloudwatch":"logs2"}'},
51
+ ])
52
+
53
+ sleep 5
54
+
55
+ d = create_driver
56
+ d.run(expect_emits: 2, timeout: 5)
57
+
58
+ emits = d.events
59
+ assert_equal(2, emits.size)
60
+ assert_equal(['test', (time_ms / 1000).floor, {'cloudwatch' => 'logs1'}], emits[0])
61
+ assert_equal(['test', (time_ms / 1000).floor, {'cloudwatch' => 'logs2'}], emits[1])
62
+ end
63
+
64
+ def test_emit_width_format
65
+ create_log_stream
66
+
67
+ time_ms = (Time.now.to_f * 1000).floor
68
+ put_log_events([
69
+ {timestamp: time_ms, message: 'logs1'},
70
+ {timestamp: time_ms, message: 'logs2'},
71
+ ])
72
+
73
+ sleep 5
74
+
75
+ d = create_driver(<<-EOC)
76
+ tag test
77
+ @type cloudwatch_logs
78
+ log_group_name #{log_group_name}
79
+ log_stream_name #{log_stream_name}
80
+ state_file /tmp/state
81
+ format /^(?<cloudwatch>[^ ]*)?/
82
+ #{aws_key_id}
83
+ #{aws_sec_key}
84
+ #{region}
85
+ #{endpoint}
86
+ EOC
87
+
88
+ d.run(expect_emits: 2, timeout: 5)
89
+
90
+ emits = d.events
91
+ assert_equal(2, emits.size)
92
+ assert_equal('test', emits[0][0])
93
+ assert_in_delta((time_ms / 1000).floor, emits[0][1], 10)
94
+ assert_equal({'cloudwatch' => 'logs1'}, emits[0][2])
95
+ assert_equal('test', emits[1][0])
96
+ assert_in_delta((time_ms / 1000).floor, emits[1][1], 10)
97
+ assert_equal({'cloudwatch' => 'logs2'}, emits[1][2])
98
+ end
99
+
100
+ def test_emit_with_prefix
101
+ new_log_stream("testprefix")
102
+ create_log_stream
103
+
104
+ time_ms = (Time.now.to_f * 1000).floor
105
+ put_log_events([
106
+ {timestamp: time_ms + 1000, message: '{"cloudwatch":"logs1"}'},
107
+ {timestamp: time_ms + 2000, message: '{"cloudwatch":"logs2"}'},
108
+ ])
109
+
110
+ new_log_stream("testprefix")
111
+ create_log_stream
112
+ put_log_events([
113
+ {timestamp: time_ms + 3000, message: '{"cloudwatch":"logs3"}'},
114
+ {timestamp: time_ms + 4000, message: '{"cloudwatch":"logs4"}'},
115
+ ])
116
+
117
+ sleep 5
118
+
119
+ d = create_driver(<<-EOC)
120
+ tag test
121
+ @type cloudwatch_logs
122
+ log_group_name #{log_group_name}
123
+ log_stream_name testprefix
124
+ use_log_stream_name_prefix true
125
+ state_file /tmp/state
126
+ #{aws_key_id}
127
+ #{aws_sec_key}
128
+ #{region}
129
+ #{endpoint}
130
+ EOC
131
+ d.run(expect_emits: 4, timeout: 5)
132
+
133
+ emits = d.events
134
+ assert_equal(4, emits.size)
135
+ assert_true(emits.include? ['test', ((time_ms + 1000) / 1000).floor, {'cloudwatch' => 'logs1'}])
136
+ assert_true(emits.include? ['test', ((time_ms + 2000) / 1000).floor, {'cloudwatch' => 'logs2'}])
137
+ assert_true(emits.include? ['test', ((time_ms + 3000) / 1000).floor, {'cloudwatch' => 'logs3'}])
138
+ assert_true(emits.include? ['test', ((time_ms + 4000) / 1000).floor, {'cloudwatch' => 'logs4'}])
139
+ end
140
+
141
+ def test_emit_with_todays_log_stream
142
+ new_log_stream("testprefix")
143
+ create_log_stream
144
+
145
+ today = DateTime.now.strftime("%Y/%m/%d")
146
+ yesterday = (Date.today - 1).strftime("%Y/%m/%d")
147
+ tomorrow = (Date.today + 1).strftime("%Y/%m/%d")
148
+
149
+
150
+ time_ms = (Time.now.to_f * 1000).floor
151
+ put_log_events([
152
+ {timestamp: time_ms + 1000, message: '{"cloudwatch":"logs1"}'},
153
+ {timestamp: time_ms + 2000, message: '{"cloudwatch":"logs2"}'},
154
+ ])
155
+
156
+ new_log_stream(today)
157
+ create_log_stream
158
+ put_log_events([
159
+ {timestamp: time_ms + 3000, message: '{"cloudwatch":"logs3"}'},
160
+ {timestamp: time_ms + 4000, message: '{"cloudwatch":"logs4"}'},
161
+ ])
162
+
163
+ new_log_stream(yesterday)
164
+ create_log_stream
165
+ put_log_events([
166
+ {timestamp: time_ms + 5000, message: '{"cloudwatch":"logs5"}'},
167
+ {timestamp: time_ms + 6000, message: '{"cloudwatch":"logs6"}'},
168
+ ])
169
+
170
+ new_log_stream(tomorrow)
171
+ create_log_stream
172
+ put_log_events([
173
+ {timestamp: time_ms + 7000, message: '{"cloudwatch":"logs7"}'},
174
+ {timestamp: time_ms + 8000, message: '{"cloudwatch":"logs8"}'},
175
+ ])
176
+
177
+ new_log_stream(today)
178
+ create_log_stream
179
+ put_log_events([
180
+ {timestamp: time_ms + 9000, message: '{"cloudwatch":"logs9"}'},
181
+ {timestamp: time_ms + 10000, message: '{"cloudwatch":"logs10"}'},
182
+ ])
183
+
184
+ new_log_stream(yesterday)
185
+ create_log_stream
186
+ put_log_events([
187
+ {timestamp: time_ms + 11000, message: '{"cloudwatch":"logs11"}'},
188
+ {timestamp: time_ms + 12000, message: '{"cloudwatch":"logs12"}'},
189
+ ])
190
+
191
+ sleep 15
192
+
193
+ d = create_driver(<<-EOC)
194
+ tag test
195
+ @type cloudwatch_logs
196
+ log_group_name #{log_group_name}
197
+ use_todays_log_stream true
198
+ state_file /tmp/state
199
+ #{aws_key_id}
200
+ #{aws_sec_key}
201
+ #{region}
202
+ #{endpoint}
203
+ EOC
204
+ d.run(expect_emits: 8, timeout: 15)
205
+
206
+ emits = d.events
207
+ assert_equal(8, emits.size)
208
+ assert_false(emits.include? ['test', ((time_ms + 1000) / 1000).floor, {'cloudwatch' => 'logs1'}])
209
+ assert_false(emits.include? ['test', ((time_ms + 2000) / 1000).floor, {'cloudwatch' => 'logs2'}])
210
+ assert_true(emits.include? ['test', ((time_ms + 3000) / 1000).floor, {'cloudwatch' => 'logs3'}])
211
+ assert_true(emits.include? ['test', ((time_ms + 4000) / 1000).floor, {'cloudwatch' => 'logs4'}])
212
+ assert_true(emits.include? ['test', ((time_ms + 5000) / 1000).floor, {'cloudwatch' => 'logs5'}])
213
+ assert_true(emits.include? ['test', ((time_ms + 6000) / 1000).floor, {'cloudwatch' => 'logs6'}])
214
+ assert_false(emits.include? ['test', ((time_ms + 7000) / 1000).floor, {'cloudwatch' => 'logs7'}])
215
+ assert_false(emits.include? ['test', ((time_ms + 8000) / 1000).floor, {'cloudwatch' => 'logs8'}])
216
+ assert_true(emits.include? ['test', ((time_ms + 9000) / 1000).floor, {'cloudwatch' => 'logs9'}])
217
+ assert_true(emits.include? ['test', ((time_ms + 10000) / 1000).floor, {'cloudwatch' => 'logs10'}])
218
+ assert_true(emits.include? ['test', ((time_ms + 11000) / 1000).floor, {'cloudwatch' => 'logs11'}])
219
+ assert_true(emits.include? ['test', ((time_ms + 12000) / 1000).floor, {'cloudwatch' => 'logs12'}])
220
+ end
221
+
222
+ private
223
+ def default_config
224
+ <<-EOC
225
+ tag test
226
+ @type cloudwatch_logs
227
+ log_group_name #{log_group_name}
228
+ log_stream_name #{log_stream_name}
229
+ state_file /tmp/state
230
+ fetch_interval 1
231
+ #{aws_key_id}
232
+ #{aws_sec_key}
233
+ #{region}
234
+ #{endpoint}
235
+ EOC
236
+ end
237
+
238
+ def create_driver(conf = default_config)
239
+ Fluent::Test::Driver::Input.new(Fluent::Plugin::CloudwatchLogsInput).configure(conf)
240
+ end
241
+ end
@@ -0,0 +1,749 @@
1
+ # coding: utf-8
2
+ require 'test_helper'
3
+ require 'fileutils'
4
+ require 'fluent/test/driver/output'
5
+ require 'fluent/test/helpers'
6
+
7
+ class CloudwatchLogsOutputTest < Test::Unit::TestCase
8
+ include CloudwatchLogsTestHelper
9
+ include Fluent::Test::Helpers
10
+
11
+ def setup
12
+ Fluent::Test.setup
13
+ require 'fluent/plugin/out_cloudwatch_logs'
14
+ end
15
+
16
+ def teardown
17
+ clear_log_group
18
+ end
19
+
20
+
21
+ def test_configure
22
+ d = create_driver(<<-EOC)
23
+ @type cloudwatch_logs
24
+ aws_key_id test_id
25
+ aws_sec_key test_key
26
+ region us-east-1
27
+ log_group_name test_group
28
+ log_stream_name test_stream
29
+ auto_create_stream false
30
+ log_group_aws_tags { "tagkey": "tagvalue", "tagkey_2": "tagvalue_2"}
31
+ retention_in_days 5
32
+ message_keys fluentd, aws, cloudwatch
33
+ EOC
34
+
35
+ assert_equal('test_id', d.instance.aws_key_id)
36
+ assert_equal('test_key', d.instance.aws_sec_key)
37
+ assert_equal('us-east-1', d.instance.region)
38
+ assert_equal('test_group', d.instance.log_group_name)
39
+ assert_equal('test_stream', d.instance.log_stream_name)
40
+ assert_equal(false, d.instance.auto_create_stream)
41
+ assert_equal("tagvalue", d.instance.log_group_aws_tags.fetch("tagkey"))
42
+ assert_equal("tagvalue_2", d.instance.log_group_aws_tags.fetch("tagkey_2"))
43
+ assert_equal(5, d.instance.retention_in_days)
44
+ assert_equal(:yajl, d.instance.json_handler)
45
+ assert_equal(["fluentd","aws","cloudwatch"], d.instance.message_keys)
46
+ end
47
+
48
+ def test_write
49
+ new_log_stream
50
+
51
+ d = create_driver
52
+ time = event_time
53
+ d.run(default_tag: fluentd_tag, flush: true) do
54
+ d.feed(time, {'cloudwatch' => 'logs1'})
55
+ # Addition converts EventTime to seconds
56
+ d.feed(time + 1, {'cloudwatch' => 'logs2'})
57
+ end
58
+
59
+ sleep 10
60
+
61
+ logs = d.logs
62
+ events = get_log_events
63
+ assert_equal(2, events.size)
64
+ assert_equal((time.to_f * 1000).floor, events[0].timestamp)
65
+ assert_equal('{"cloudwatch":"logs1"}', events[0].message)
66
+ assert_equal((time.to_i + 1) * 1000, events[1].timestamp)
67
+ assert_equal('{"cloudwatch":"logs2"}', events[1].message)
68
+
69
+ assert(logs.any?{|log| log.include?("Called PutLogEvents API") })
70
+ end
71
+
72
+ def test_write_utf8
73
+ new_log_stream
74
+
75
+ d = create_driver
76
+ time = event_time
77
+ d.run(default_tag: fluentd_tag) do
78
+ d.feed(time, { 'cloudwatch' => 'これは日本語です'.force_encoding('UTF-8')})
79
+ end
80
+
81
+ sleep 10
82
+
83
+ events = get_log_events
84
+ assert_equal(1, events.size)
85
+ assert_equal((time.to_f * 1000).floor, events[0].timestamp)
86
+ assert_equal('{"cloudwatch":"これは日本語です"}', events[0].message)
87
+ end
88
+
89
+ def test_write_24h_apart
90
+ new_log_stream
91
+
92
+ d = create_driver(<<-EOC)
93
+ #{default_config}
94
+ log_group_name #{log_group_name}
95
+ log_stream_name #{log_stream_name}
96
+ utc
97
+ EOC
98
+ time = event_time
99
+ d.run(default_tag: fluentd_tag) do
100
+ d.feed(time - 60 * 60 * 25, {'cloudwatch' => 'logs0'})
101
+ d.feed(time, {'cloudwatch' => 'logs1'})
102
+ d.feed(time + 1, {'cloudwatch' => 'logs2'})
103
+ end
104
+
105
+ sleep 10
106
+
107
+ events = get_log_events
108
+ assert_equal(3, events.size)
109
+ assert_equal((time.to_i - 60 * 60 * 25) * 1000, events[0].timestamp)
110
+ assert_equal('{"cloudwatch":"logs0"}', events[0].message)
111
+ assert_equal((time.to_f * 1000).floor, events[1].timestamp)
112
+ assert_equal('{"cloudwatch":"logs1"}', events[1].message)
113
+ assert_equal((time.to_i + 1) * 1000, events[2].timestamp)
114
+ assert_equal('{"cloudwatch":"logs2"}', events[2].message)
115
+ end
116
+
117
+ def test_write_with_message_keys
118
+ new_log_stream
119
+
120
+ d = create_driver(<<-EOC)
121
+ #{default_config}
122
+ message_keys message,cloudwatch
123
+ log_group_name #{log_group_name}
124
+ log_stream_name #{log_stream_name}
125
+ EOC
126
+
127
+ time = event_time
128
+ d.run(default_tag: fluentd_tag) do
129
+ d.feed(time, {'cloudwatch' => 'logs1', 'message' => 'message1'})
130
+ d.feed(time + 1, {'cloudwatch' => 'logs2', 'message' => 'message2'})
131
+ end
132
+
133
+ sleep 10
134
+
135
+ events = get_log_events
136
+ assert_equal(2, events.size)
137
+ assert_equal((time.to_f * 1000).floor, events[0].timestamp)
138
+ assert_equal('message1 logs1', events[0].message)
139
+ assert_equal((time.to_i + 1) * 1000, events[1].timestamp)
140
+ assert_equal('message2 logs2', events[1].message)
141
+ end
142
+
143
+ def test_write_with_max_message_length
144
+ new_log_stream
145
+
146
+ d = create_driver(<<-EOC)
147
+ #{default_config}
148
+ message_keys message,cloudwatch
149
+ max_message_length 10
150
+ log_group_name #{log_group_name}
151
+ log_stream_name #{log_stream_name}
152
+ EOC
153
+
154
+ time = event_time
155
+ d.run(default_tag: fluentd_tag) do
156
+ d.feed(time, {'cloudwatch' => 'logs1', 'message' => 'message1'})
157
+ d.feed(time + 1, {'cloudwatch' => 'logs2', 'message' => 'message2'})
158
+ end
159
+
160
+ sleep 10
161
+
162
+ events = get_log_events
163
+ assert_equal(2, events.size)
164
+ assert_equal((time.to_f * 1000).floor, events[0].timestamp)
165
+ assert_equal('message1 l', events[0].message)
166
+ assert_equal((time.to_i + 1) * 1000, events[1].timestamp)
167
+ assert_equal('message2 l', events[1].message)
168
+ end
169
+
170
+ def test_write_use_tag_as_group
171
+ new_log_stream
172
+
173
+ d = create_driver(<<-EOC)
174
+ #{default_config}
175
+ message_keys message,cloudwatch
176
+ use_tag_as_group true
177
+ log_stream_name #{log_stream_name}
178
+ EOC
179
+
180
+ time = event_time
181
+ d.run(default_tag: fluentd_tag) do
182
+ d.feed(time, {'cloudwatch' => 'logs1', 'message' => 'message1'})
183
+ d.feed(time + 1, {'cloudwatch' => 'logs2', 'message' => 'message2'})
184
+ end
185
+
186
+ sleep 10
187
+
188
+ events = get_log_events(fluentd_tag)
189
+ assert_equal(2, events.size)
190
+ assert_equal((time.to_f * 1000).floor, events[0].timestamp)
191
+ assert_equal('message1 logs1', events[0].message)
192
+ assert_equal((time.to_i + 1) * 1000, events[1].timestamp)
193
+ assert_equal('message2 logs2', events[1].message)
194
+ end
195
+
196
+ def test_write_use_tag_as_stream
197
+ new_log_stream
198
+
199
+ d = create_driver(<<-EOC)
200
+ #{default_config}
201
+ message_keys message,cloudwatch
202
+ use_tag_as_stream true
203
+ log_group_name #{log_group_name}
204
+ EOC
205
+
206
+ time = event_time
207
+ d.run(default_tag: fluentd_tag) do
208
+ d.feed(time, {'cloudwatch' => 'logs1', 'message' => 'message1'})
209
+ d.feed(time + 1, {'cloudwatch' => 'logs2', 'message' => 'message2'})
210
+ end
211
+
212
+ sleep 10
213
+
214
+ events = get_log_events(log_group_name, fluentd_tag)
215
+ assert_equal(2, events.size)
216
+ assert_equal((time.to_f * 1000).floor, events[0].timestamp)
217
+ assert_equal('message1 logs1', events[0].message)
218
+ assert_equal((time.to_i + 1) * 1000, events[1].timestamp)
219
+ assert_equal('message2 logs2', events[1].message)
220
+ end
221
+
222
+ def test_write_use_placeholders
223
+ new_log_stream
224
+
225
+ config = {'@type' => 'cloudwatch_logs',
226
+ 'auto_create_stream' => true,
227
+ 'message_keys' => ["message","cloudwatch"],
228
+ 'log_stream_name' => "${tag}",
229
+ 'log_group_name' => log_group_name}
230
+ config.merge!(config_elementify(aws_key_id)) if aws_key_id
231
+ config.merge!(config_elementify(aws_sec_key)) if aws_sec_key
232
+ config.merge!(config_elementify(region)) if region
233
+ config.merge!(config_elementify(endpoint)) if endpoint
234
+
235
+ d = create_driver(
236
+ Fluent::Config::Element.new('ROOT', '', config,[
237
+ Fluent::Config::Element.new('buffer', 'tag, time', {
238
+ '@type' => 'memory',
239
+ 'timekey' => 3600
240
+ }, [])
241
+ ])
242
+ )
243
+
244
+ time = event_time
245
+ d.run(default_tag: fluentd_tag) do
246
+ d.feed(time, {'cloudwatch' => 'logs1', 'message' => 'message1'})
247
+ d.feed(time + 1, {'cloudwatch' => 'logs2', 'message' => 'message2'})
248
+ end
249
+
250
+ sleep 10
251
+
252
+ events = get_log_events(log_group_name, fluentd_tag)
253
+ assert_equal(2, events.size)
254
+ assert_equal((time.to_f * 1000).floor, events[0].timestamp)
255
+ assert_equal('message1 logs1', events[0].message)
256
+ assert_equal((time.to_i + 1) * 1000, events[1].timestamp)
257
+ assert_equal('message2 logs2', events[1].message)
258
+ end
259
+
260
+ def test_write_use_placeholders_parts
261
+ new_log_stream
262
+
263
+ config = {'@type' => 'cloudwatch_logs',
264
+ 'auto_create_stream' => true,
265
+ 'message_keys' => ["message","cloudwatch"],
266
+ 'log_stream_name' => "${tag[0]}-${tag[1]}-${tag[2]}-${tag[3]}",
267
+ 'log_group_name' => log_group_name}
268
+ config.merge!(config_elementify(aws_key_id)) if aws_key_id
269
+ config.merge!(config_elementify(aws_sec_key)) if aws_sec_key
270
+ config.merge!(config_elementify(region)) if region
271
+ config.merge!(config_elementify(endpoint)) if endpoint
272
+
273
+ d = create_driver(
274
+ Fluent::Config::Element.new('ROOT', '', config,[
275
+ Fluent::Config::Element.new('buffer', 'tag, time', {
276
+ '@type' => 'memory',
277
+ 'timekey' => 3600
278
+ }, [])
279
+ ])
280
+ )
281
+
282
+ time = event_time
283
+ d.run(default_tag: fluentd_tag) do
284
+ d.feed(time, {'cloudwatch' => 'logs1', 'message' => 'message1'})
285
+ d.feed(time + 1, {'cloudwatch' => 'logs2', 'message' => 'message2'})
286
+ end
287
+
288
+ sleep 10
289
+
290
+ events = get_log_events(log_group_name, 'fluent-plugin-cloudwatch-test')
291
+ assert_equal(2, events.size)
292
+ assert_equal((time.to_f * 1000).floor, events[0].timestamp)
293
+ assert_equal('message1 logs1', events[0].message)
294
+ assert_equal((time.to_i + 1) * 1000, events[1].timestamp)
295
+ assert_equal('message2 logs2', events[1].message)
296
+ end
297
+
298
+ def test_write_use_time_placeholders
299
+ new_log_stream
300
+
301
+ config = {'@type' => 'cloudwatch_logs',
302
+ 'auto_create_stream' => true,
303
+ 'message_keys' => ["message","cloudwatch"],
304
+ 'log_stream_name' => "fluent-plugin-cloudwatch-test-%Y%m%d",
305
+ 'log_group_name' => log_group_name}
306
+ config.merge!(config_elementify(aws_key_id)) if aws_key_id
307
+ config.merge!(config_elementify(aws_sec_key)) if aws_sec_key
308
+ config.merge!(config_elementify(region)) if region
309
+ config.merge!(config_elementify(endpoint)) if endpoint
310
+
311
+ d = create_driver(
312
+ Fluent::Config::Element.new('ROOT', '', config,[
313
+ Fluent::Config::Element.new('buffer', 'tag, time', {
314
+ '@type' => 'memory',
315
+ 'timekey' => 3600
316
+ }, [])
317
+ ])
318
+ )
319
+
320
+ time = event_time
321
+ d.run(default_tag: fluentd_tag) do
322
+ d.feed(time, {'cloudwatch' => 'logs1', 'message' => 'message1'})
323
+ d.feed(time + 1, {'cloudwatch' => 'logs2', 'message' => 'message2'})
324
+ end
325
+
326
+ sleep 10
327
+
328
+ events = get_log_events(log_group_name, "fluent-plugin-cloudwatch-test-#{Time.at(time).strftime("%Y%m%d")}")
329
+ assert_equal(2, events.size)
330
+ assert_equal((time.to_f * 1000).floor, events[0].timestamp)
331
+ assert_equal('message1 logs1', events[0].message)
332
+ assert_equal((time.to_i + 1) * 1000, events[1].timestamp)
333
+ assert_equal('message2 logs2', events[1].message)
334
+ end
335
+
336
+ def test_include_time_key
337
+ new_log_stream
338
+
339
+ d = create_driver(<<-EOC)
340
+ #{default_config}
341
+ include_time_key true
342
+ log_group_name #{log_group_name}
343
+ log_stream_name #{log_stream_name}
344
+ utc
345
+ EOC
346
+
347
+ time = event_time
348
+ d.run(default_tag: fluentd_tag) do
349
+ d.feed(time, {'cloudwatch' => 'logs1'})
350
+ d.feed(time + 1, {'cloudwatch' => 'logs2'})
351
+ end
352
+
353
+ sleep 10
354
+
355
+ events = get_log_events
356
+ assert_equal(2, events.size)
357
+ assert_equal((time.to_f * 1000).floor, events[0].timestamp)
358
+ assert_equal("{\"cloudwatch\":\"logs1\",\"time\":\"#{Time.at(time.to_r).utc.strftime("%Y-%m-%dT%H:%M:%SZ")}\"}", events[0].message)
359
+ assert_equal((time.to_i + 1) * 1000, events[1].timestamp)
360
+ assert_equal("{\"cloudwatch\":\"logs2\",\"time\":\"#{Time.at((time+1).to_r).utc.strftime("%Y-%m-%dT%H:%M:%SZ")}\"}", events[1].message)
361
+ end
362
+
363
+ def test_include_time_key_localtime
364
+ new_log_stream
365
+
366
+ d = create_driver(<<-EOC)
367
+ #{default_config}
368
+ include_time_key true
369
+ localtime true
370
+ log_group_name #{log_group_name}
371
+ log_stream_name #{log_stream_name}
372
+ EOC
373
+
374
+ time = event_time
375
+ d.run(default_tag: fluentd_tag) do
376
+ d.feed(time, {'cloudwatch' => 'logs1'})
377
+ d.feed(time + 1, {'cloudwatch' => 'logs2'})
378
+ end
379
+
380
+ sleep 10
381
+
382
+ events = get_log_events
383
+ assert_equal(2, events.size)
384
+ assert_equal((time.to_f * 1000).floor, events[0].timestamp)
385
+ assert_equal("{\"cloudwatch\":\"logs1\",\"time\":\"#{Time.at(time.to_r).strftime("%Y-%m-%dT%H:%M:%S%:z")}\"}", events[0].message)
386
+ assert_equal((time.to_i + 1) * 1000, events[1].timestamp)
387
+ assert_equal("{\"cloudwatch\":\"logs2\",\"time\":\"#{Time.at((time+1).to_r).to_time.strftime("%Y-%m-%dT%H:%M:%S%:z")}\"}", events[1].message)
388
+ end
389
+
390
+ def test_log_group_name_key_and_log_stream_name_key
391
+ new_log_stream
392
+
393
+ d = create_driver(<<-EOC)
394
+ #{default_config}
395
+ log_group_name_key group_name_key
396
+ log_stream_name_key stream_name_key
397
+ @log_level debug
398
+ EOC
399
+
400
+ stream1 = new_log_stream
401
+ stream2 = new_log_stream
402
+
403
+ records = [
404
+ {'cloudwatch' => 'logs1', 'message' => 'message1', 'group_name_key' => log_group_name, 'stream_name_key' => stream1},
405
+ {'cloudwatch' => 'logs2', 'message' => 'message1', 'group_name_key' => log_group_name, 'stream_name_key' => stream2},
406
+ {'cloudwatch' => 'logs3', 'message' => 'message1', 'group_name_key' => log_group_name, 'stream_name_key' => stream1},
407
+ ]
408
+
409
+ time = event_time
410
+ d.run(default_tag: fluentd_tag) do
411
+ records.each_with_index do |record, i|
412
+ d.feed(time + i, record)
413
+ end
414
+ end
415
+
416
+ logs = d.logs
417
+ # Call API once for each stream
418
+ assert_equal(2, logs.select {|l| l =~ /Called PutLogEvents API/ }.size)
419
+
420
+ sleep 10
421
+
422
+ events = get_log_events(log_group_name, stream1)
423
+ assert_equal(2, events.size)
424
+ assert_equal(time.to_i * 1000, events[0].timestamp)
425
+ assert_equal((time.to_i + 2) * 1000, events[1].timestamp)
426
+ assert_equal(records[0], JSON.parse(events[0].message))
427
+ assert_equal(records[2], JSON.parse(events[1].message))
428
+
429
+ events = get_log_events(log_group_name, stream2)
430
+ assert_equal(1, events.size)
431
+ assert_equal((time.to_i + 1) * 1000, events[0].timestamp)
432
+ assert_equal(records[1], JSON.parse(events[0].message))
433
+ end
434
+
435
+ def test_remove_log_group_name_key_and_remove_log_stream_name_key
436
+ new_log_stream
437
+
438
+ d = create_driver(<<-EOC)
439
+ #{default_config}
440
+ log_group_name_key group_name_key
441
+ log_stream_name_key stream_name_key
442
+ remove_log_group_name_key true
443
+ remove_log_stream_name_key true
444
+ EOC
445
+
446
+ time = event_time
447
+ d.run(default_tag: fluentd_tag) do
448
+ d.feed(time, {'cloudwatch' => 'logs1', 'message' => 'message1', 'group_name_key' => log_group_name, 'stream_name_key' => log_stream_name})
449
+ end
450
+
451
+ sleep 10
452
+
453
+ events = get_log_events(log_group_name, log_stream_name)
454
+ assert_equal(1, events.size)
455
+ assert_equal((time.to_f * 1000).floor, events[0].timestamp)
456
+ assert_equal({'cloudwatch' => 'logs1', 'message' => 'message1'}, JSON.parse(events[0].message))
457
+ end
458
+
459
+ def test_log_group_aws_tags
460
+ clear_log_group
461
+
462
+ d = create_driver(<<-EOC)
463
+ #{default_config}
464
+ auto_create_stream true
465
+ use_tag_as_stream true
466
+ log_group_name_key group_name_key
467
+ log_group_aws_tags {"tag1": "value1", "tag2": "value2"}
468
+ EOC
469
+
470
+ records = [
471
+ {'cloudwatch' => 'logs1', 'message' => 'message1', 'group_name_key' => log_group_name},
472
+ {'cloudwatch' => 'logs2', 'message' => 'message1', 'group_name_key' => log_group_name},
473
+ {'cloudwatch' => 'logs3', 'message' => 'message1', 'group_name_key' => log_group_name},
474
+ ]
475
+
476
+ time = Time.now
477
+ d.run(default_tag: fluentd_tag) do
478
+ records.each_with_index do |record, i|
479
+ d.feed(time.to_i + i, record)
480
+ end
481
+ end
482
+
483
+ awstags = get_log_group_tags
484
+ assert_equal("value1", awstags.fetch("tag1"))
485
+ assert_equal("value2", awstags.fetch("tag2"))
486
+ end
487
+
488
+ def test_retention_in_days
489
+ clear_log_group
490
+
491
+ d = create_driver(<<-EOC)
492
+ #{default_config}
493
+ auto_create_stream true
494
+ use_tag_as_stream true
495
+ log_group_name_key group_name_key
496
+ retention_in_days 7
497
+ EOC
498
+
499
+ records = [
500
+ {'cloudwatch' => 'logs1', 'message' => 'message1', 'group_name_key' => log_group_name},
501
+ {'cloudwatch' => 'logs2', 'message' => 'message1', 'group_name_key' => log_group_name},
502
+ {'cloudwatch' => 'logs3', 'message' => 'message1', 'group_name_key' => log_group_name},
503
+ ]
504
+
505
+ time = Time.now
506
+ d.run(default_tag: fluentd_tag) do
507
+ records.each_with_index do |record, i|
508
+ d.feed(time.to_i + i, record)
509
+ end
510
+ end
511
+
512
+ retention = get_log_group_retention_days
513
+ assert_equal(d.instance.retention_in_days, retention)
514
+ end
515
+
516
+ def test_invalid_retention_in_days
517
+ clear_log_group
518
+
519
+ d = create_driver(<<-EOC)
520
+ #{default_config}
521
+ auto_create_stream true
522
+ use_tag_as_stream true
523
+ log_group_name_key group_name_key
524
+ retention_in_days 4
525
+ EOC
526
+
527
+ records = [
528
+ {'cloudwatch' => 'logs1', 'message' => 'message1', 'group_name_key' => log_group_name},
529
+ {'cloudwatch' => 'logs2', 'message' => 'message1', 'group_name_key' => log_group_name},
530
+ {'cloudwatch' => 'logs3', 'message' => 'message1', 'group_name_key' => log_group_name},
531
+ ]
532
+
533
+ time = Time.now
534
+ d.run(default_tag: fluentd_tag) do
535
+ records.each_with_index do |record, i|
536
+ d.feed(time.to_i + i, record)
537
+ end
538
+ end
539
+
540
+ assert_match(/failed to set retention policy for Log group/, d.logs[0])
541
+ end
542
+
543
+ def test_log_group_aws_tags_key
544
+ clear_log_group
545
+
546
+ d = create_driver(<<-EOC)
547
+ #{default_config}
548
+ auto_create_stream true
549
+ use_tag_as_stream true
550
+ log_group_name_key group_name_key
551
+ log_group_aws_tags_key aws_tags
552
+ EOC
553
+
554
+ records = [
555
+ {'cloudwatch' => 'logs1', 'message' => 'message1', 'group_name_key' => log_group_name, 'aws_tags' => {"tag1" => "value1", "tag2" => "value2"}},
556
+ {'cloudwatch' => 'logs2', 'message' => 'message1', 'group_name_key' => log_group_name, 'aws_tags' => {"tag1" => "value1", "tag2" => "value2"}},
557
+ {'cloudwatch' => 'logs3', 'message' => 'message1', 'group_name_key' => log_group_name, 'aws_tags' => {"tag1" => "value1", "tag2" => "value2"}}
558
+ ]
559
+
560
+ time = Time.now
561
+ d.run(default_tag: fluentd_tag) do
562
+ records.each_with_index do |record, i|
563
+ d.feed(time.to_i + i, record)
564
+ end
565
+ end
566
+
567
+ awstags = get_log_group_tags
568
+ assert_equal("value1", awstags.fetch("tag1"))
569
+ assert_equal("value2", awstags.fetch("tag2"))
570
+ end
571
+
572
+ def test_log_group_aws_tags_key_same_group_diff_tags
573
+ clear_log_group
574
+
575
+ d = create_driver(<<-EOC)
576
+ #{default_config}
577
+ auto_create_stream true
578
+ use_tag_as_stream true
579
+ log_group_name_key group_name_key
580
+ log_group_aws_tags_key aws_tags
581
+ EOC
582
+
583
+ records = [
584
+ {'cloudwatch' => 'logs1', 'message' => 'message1', 'group_name_key' => log_group_name, 'aws_tags' => {"tag1" => "value1", "tag2" => "value2"}},
585
+ {'cloudwatch' => 'logs3', 'message' => 'message1', 'group_name_key' => log_group_name, 'aws_tags' => {"tag3" => "value3", "tag4" => "value4"}}
586
+ ]
587
+
588
+ time = Time.now
589
+ d.run(default_tag: fluentd_tag) do
590
+ records.each_with_index do |record, i|
591
+ d.feed(time.to_i + i, record)
592
+ end
593
+ end
594
+
595
+ awstags = get_log_group_tags
596
+ assert_equal("value1", awstags.fetch("tag1"))
597
+ assert_equal("value2", awstags.fetch("tag2"))
598
+ assert_raise KeyError do
599
+ awstags.fetch("tag3")
600
+ end
601
+ assert_raise KeyError do
602
+ awstags.fetch("tag4")
603
+ end
604
+ end
605
+
606
+ def test_log_group_aws_tags_key_no_tags
607
+ clear_log_group
608
+
609
+ d = create_driver(<<-EOC)
610
+ #{default_config}
611
+ auto_create_stream true
612
+ log_group_name_key group_name_key
613
+ log_stream_name_key stream_name_key
614
+ remove_log_group_name_key true
615
+ remove_log_stream_name_key true
616
+ log_group_aws_tags_key aws_tags
617
+ EOC
618
+
619
+ stream = log_stream_name
620
+ records = [
621
+ {'cloudwatch' => 'logs1', 'message' => 'message1', 'group_name_key' => log_group_name, 'stream_name_key' => stream},
622
+ {'cloudwatch' => 'logs2', 'message' => 'message2', 'group_name_key' => log_group_name, 'stream_name_key' => stream}
623
+ ]
624
+
625
+ time = Time.now
626
+ d.run(default_tag: fluentd_tag) do
627
+ records.each_with_index do |record, i|
628
+ d.feed(time.to_i + i, record)
629
+ end
630
+ end
631
+
632
+ sleep 10
633
+
634
+ awstags = get_log_group_tags
635
+
636
+ assert_raise KeyError do
637
+ awstags.fetch("tag1")
638
+ end
639
+
640
+ events = get_log_events(log_group_name, stream)
641
+ assert_equal(2, events.size)
642
+ assert_equal(time.to_i * 1000, events[0].timestamp)
643
+ assert_equal({'cloudwatch' => 'logs1', 'message' => 'message1'}, JSON.parse(events[0].message))
644
+ assert_equal({'cloudwatch' => 'logs2', 'message' => 'message2'}, JSON.parse(events[1].message))
645
+ end
646
+
647
+ def test_retrying_on_throttling_exception
648
+ resp = mock()
649
+ resp.expects(:rejected_log_events_info)
650
+ resp.expects(:next_sequence_token)
651
+ client = Aws::CloudWatchLogs::Client.new
652
+ client.stubs(:put_log_events).
653
+ raises(Aws::CloudWatchLogs::Errors::ThrottlingException.new(nil, "error")).then.returns(resp)
654
+
655
+ d = create_driver
656
+ time = event_time
657
+ d.instance.instance_variable_set(:@logs, client)
658
+ d.run(default_tag: fluentd_tag) do
659
+ d.feed(time, {'message' => 'message1'})
660
+ end
661
+
662
+ logs = d.logs
663
+ assert_equal(1, logs.select {|l| l =~ /Called PutLogEvents API/ }.size)
664
+ assert_equal(1, logs.select {|l| l =~ /failed to PutLogEvents/ }.size)
665
+ assert_equal(1, logs.select {|l| l =~ /retry succeeded/ }.size)
666
+ end
667
+
668
+ def test_retrying_on_throttling_exception_and_throw_away
669
+ client = Aws::CloudWatchLogs::Client.new
670
+ client.stubs(:put_log_events).
671
+ raises(Aws::CloudWatchLogs::Errors::ThrottlingException.new(nil, "error"))
672
+ time = Fluent::Engine.now
673
+ d = create_driver(<<-EOC)
674
+ #{default_config}
675
+ log_group_name #{log_group_name}
676
+ log_stream_name #{log_stream_name}
677
+ put_log_events_retry_limit 1
678
+ @log_level debug
679
+ EOC
680
+ d.instance.instance_variable_set(:@logs, client)
681
+ d.run(default_tag: fluentd_tag) do
682
+ d.feed(time, {'message' => 'message1'})
683
+ end
684
+
685
+ logs = d.logs
686
+ assert_equal(0, logs.select {|l| l =~ /Called PutLogEvents API/ }.size)
687
+ assert_equal(3, logs.select {|l| l =~ /failed to PutLogEvents/ }.size)
688
+ assert_equal(1, logs.select {|l| l =~ /failed to PutLogEvents and discard logs/ }.size)
689
+ end
690
+
691
+ def test_too_large_event
692
+ time = Fluent::Engine.now
693
+ d = create_driver(<<-EOC)
694
+ #{default_config}
695
+ log_group_name #{log_group_name}
696
+ log_stream_name #{log_stream_name}
697
+ @log_level debug
698
+ EOC
699
+ d.run(default_tag: fluentd_tag) do
700
+ d.feed(time, {'message' => '*' * 256 * 1024})
701
+ end
702
+
703
+ logs = d.logs
704
+ assert(logs.any?{|log| log.include?("Log event is discarded because it is too large: 262184 bytes exceeds limit of 262144")})
705
+ end
706
+
707
+ def test_scrub_record
708
+ record = {
709
+ "hash" => {
710
+ "str" => "\xAE",
711
+ },
712
+ "array" => [
713
+ "\xAE",
714
+ ],
715
+ "str" => "\xAE",
716
+ }
717
+
718
+ d = create_driver
719
+ d.instance.send(:scrub_record!, record)
720
+
721
+ assert_equal("�", record["hash"]["str"])
722
+ assert_equal("�", record["array"][0])
723
+ assert_equal("�", record["str"])
724
+ end
725
+
726
+ private
727
+ def default_config
728
+ <<-EOC
729
+ @type cloudwatch_logs
730
+ auto_create_stream true
731
+ #{aws_key_id}
732
+ #{aws_sec_key}
733
+ #{region}
734
+ #{endpoint}
735
+ EOC
736
+ end
737
+
738
+ def create_driver(conf = nil)
739
+ unless conf
740
+ conf = <<-EOC
741
+ #{default_config}
742
+ log_group_name #{log_group_name}
743
+ log_stream_name #{log_stream_name}
744
+ @log_level debug
745
+ EOC
746
+ end
747
+ Fluent::Test::Driver::Output.new(Fluent::Plugin::CloudwatchLogsOutput).configure(conf)
748
+ end
749
+ end