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

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.
@@ -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