fluent-plugin-cloudwatch-logs-yajl 0.4.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,409 @@
1
+ require 'test_helper'
2
+ require 'fileutils'
3
+
4
+ class CloudwatchLogsOutputTest < Test::Unit::TestCase
5
+ include CloudwatchLogsTestHelper
6
+
7
+ def setup
8
+ Fluent::Test.setup
9
+ require 'fluent/plugin/out_cloudwatch_logs'
10
+
11
+ end
12
+
13
+ def teardown
14
+ clear_log_group
15
+ end
16
+
17
+
18
+ def test_configure
19
+ d = create_driver(<<-EOC)
20
+ type cloudwatch_logs
21
+ aws_key_id test_id
22
+ aws_sec_key test_key
23
+ region us-east-1
24
+ log_group_name test_group
25
+ log_stream_name test_stream
26
+ auto_create_stream false
27
+ EOC
28
+
29
+ assert_equal('test_id', d.instance.aws_key_id)
30
+ assert_equal('test_key', d.instance.aws_sec_key)
31
+ assert_equal('us-east-1', d.instance.region)
32
+ assert_equal('test_group', d.instance.log_group_name)
33
+ assert_equal('test_stream', d.instance.log_stream_name)
34
+ assert_equal(false, d.instance.auto_create_stream)
35
+ end
36
+
37
+ def test_write
38
+ new_log_stream
39
+
40
+ d = create_driver
41
+ time = Time.now
42
+ d.emit({'cloudwatch' => 'logs1'}, time.to_i)
43
+ d.emit({'cloudwatch' => 'logs2'}, time.to_i + 1)
44
+ d.run
45
+
46
+ sleep 10
47
+
48
+ events = get_log_events
49
+ assert_equal(2, events.size)
50
+ assert_equal(time.to_i * 1000, events[0].timestamp)
51
+ assert_equal('{"cloudwatch":"logs1"}', events[0].message)
52
+ assert_equal((time.to_i + 1) * 1000, events[1].timestamp)
53
+ assert_equal('{"cloudwatch":"logs2"}', events[1].message)
54
+
55
+ assert_match(/Calling PutLogEvents API/, d.instance.log.logs[0])
56
+ end
57
+
58
+ def test_write_utf8
59
+ new_log_stream
60
+
61
+ d = create_driver
62
+ time = Time.now
63
+ d.emit({'cloudwatch' => 'これは日本語です'.force_encoding('UTF-8')}, time.to_i)
64
+ d.run
65
+
66
+ sleep 10
67
+
68
+ events = get_log_events
69
+ assert_equal(1, events.size)
70
+ assert_equal(time.to_i * 1000, events[0].timestamp)
71
+ assert_equal('{"cloudwatch":"これは日本語です"}', events[0].message)
72
+ end
73
+
74
+ def test_write_24h_apart
75
+ new_log_stream
76
+
77
+ d = create_driver
78
+ time = Time.now
79
+ d.emit({'cloudwatch' => 'logs0'}, time.to_i - 60 * 60 * 25)
80
+ d.emit({'cloudwatch' => 'logs1'}, time.to_i)
81
+ d.emit({'cloudwatch' => 'logs2'}, time.to_i + 1)
82
+ d.run
83
+
84
+ sleep 10
85
+
86
+ events = get_log_events
87
+ assert_equal(3, events.size)
88
+ assert_equal((time.to_i - 60 * 60 * 25) * 1000, events[0].timestamp)
89
+ assert_equal('{"cloudwatch":"logs0"}', events[0].message)
90
+ assert_equal((time.to_i ) * 1000, events[1].timestamp)
91
+ assert_equal('{"cloudwatch":"logs1"}', events[1].message)
92
+ assert_equal((time.to_i + 1) * 1000, events[2].timestamp)
93
+ assert_equal('{"cloudwatch":"logs2"}', events[2].message)
94
+ end
95
+
96
+ def test_write_with_message_keys
97
+ new_log_stream
98
+
99
+ d = create_driver(<<-EOC)
100
+ #{default_config}
101
+ message_keys message,cloudwatch
102
+ log_group_name #{log_group_name}
103
+ log_stream_name #{log_stream_name}
104
+ EOC
105
+
106
+ time = Time.now
107
+ d.emit({'cloudwatch' => 'logs1', 'message' => 'message1'}, time.to_i)
108
+ d.emit({'cloudwatch' => 'logs2', 'message' => 'message2'}, time.to_i + 1)
109
+ d.run
110
+
111
+ sleep 10
112
+
113
+ events = get_log_events
114
+ assert_equal(2, events.size)
115
+ assert_equal(time.to_i * 1000, events[0].timestamp)
116
+ assert_equal('message1 logs1', events[0].message)
117
+ assert_equal((time.to_i + 1) * 1000, events[1].timestamp)
118
+ assert_equal('message2 logs2', events[1].message)
119
+ end
120
+
121
+ def test_write_with_max_message_length
122
+ new_log_stream
123
+
124
+ d = create_driver(<<-EOC)
125
+ #{default_config}
126
+ message_keys message,cloudwatch
127
+ max_message_length 10
128
+ log_group_name #{log_group_name}
129
+ log_stream_name #{log_stream_name}
130
+ EOC
131
+
132
+ time = Time.now
133
+ d.emit({'cloudwatch' => 'logs1', 'message' => 'message1'}, time.to_i)
134
+ d.emit({'cloudwatch' => 'logs2', 'message' => 'message2'}, time.to_i + 1)
135
+ d.run
136
+
137
+ sleep 10
138
+
139
+ events = get_log_events
140
+ assert_equal(2, events.size)
141
+ assert_equal(time.to_i * 1000, events[0].timestamp)
142
+ assert_equal('message1 l', events[0].message)
143
+ assert_equal((time.to_i + 1) * 1000, events[1].timestamp)
144
+ assert_equal('message2 l', events[1].message)
145
+ end
146
+
147
+ def test_write_use_tag_as_group
148
+ new_log_stream
149
+
150
+ d = create_driver(<<-EOC)
151
+ #{default_config}
152
+ message_keys message,cloudwatch
153
+ use_tag_as_group true
154
+ log_stream_name #{log_stream_name}
155
+ EOC
156
+
157
+ time = Time.now
158
+ d.emit({'cloudwatch' => 'logs1', 'message' => 'message1'}, time.to_i)
159
+ d.emit({'cloudwatch' => 'logs2', 'message' => 'message2'}, time.to_i + 1)
160
+ d.run
161
+
162
+ sleep 10
163
+
164
+ events = get_log_events(fluentd_tag)
165
+ assert_equal(2, events.size)
166
+ assert_equal(time.to_i * 1000, events[0].timestamp)
167
+ assert_equal('message1 logs1', events[0].message)
168
+ assert_equal((time.to_i + 1) * 1000, events[1].timestamp)
169
+ assert_equal('message2 logs2', events[1].message)
170
+ end
171
+
172
+ def test_write_use_tag_as_stream
173
+ new_log_stream
174
+
175
+ d = create_driver(<<-EOC)
176
+ #{default_config}
177
+ message_keys message,cloudwatch
178
+ use_tag_as_stream true
179
+ log_group_name #{log_group_name}
180
+ EOC
181
+
182
+ time = Time.now
183
+ d.emit({'cloudwatch' => 'logs1', 'message' => 'message1'}, time.to_i)
184
+ d.emit({'cloudwatch' => 'logs2', 'message' => 'message2'}, time.to_i + 1)
185
+ d.run
186
+
187
+ sleep 10
188
+
189
+ events = get_log_events(log_group_name, fluentd_tag)
190
+ assert_equal(2, events.size)
191
+ assert_equal(time.to_i * 1000, events[0].timestamp)
192
+ assert_equal('message1 logs1', events[0].message)
193
+ assert_equal((time.to_i + 1) * 1000, events[1].timestamp)
194
+ assert_equal('message2 logs2', events[1].message)
195
+ end
196
+
197
+ def test_include_time_key
198
+ new_log_stream
199
+
200
+ d = create_driver(<<-EOC)
201
+ #{default_config}
202
+ include_time_key true
203
+ log_group_name #{log_group_name}
204
+ log_stream_name #{log_stream_name}
205
+ EOC
206
+
207
+ time = Time.now
208
+ d.emit({'cloudwatch' => 'logs1'}, time.to_i)
209
+ d.emit({'cloudwatch' => 'logs2'}, time.to_i + 1)
210
+ d.run
211
+
212
+ sleep 10
213
+
214
+ events = get_log_events
215
+ assert_equal(2, events.size)
216
+ assert_equal(time.to_i * 1000, events[0].timestamp)
217
+ assert_equal("{\"cloudwatch\":\"logs1\",\"time\":\"#{time.utc.strftime("%Y-%m-%dT%H:%M:%SZ")}\"}", events[0].message)
218
+ assert_equal((time.to_i + 1) * 1000, events[1].timestamp)
219
+ assert_equal("{\"cloudwatch\":\"logs2\",\"time\":\"#{(time+1).utc.strftime("%Y-%m-%dT%H:%M:%SZ")}\"}", events[1].message)
220
+ end
221
+
222
+ def test_include_time_key_localtime
223
+ new_log_stream
224
+
225
+ d = create_driver(<<-EOC)
226
+ #{default_config}
227
+ include_time_key true
228
+ localtime true
229
+ log_group_name #{log_group_name}
230
+ log_stream_name #{log_stream_name}
231
+ EOC
232
+
233
+ time = Time.now
234
+ d.emit({'cloudwatch' => 'logs1'}, time.to_i)
235
+ d.emit({'cloudwatch' => 'logs2'}, time.to_i + 1)
236
+ d.run
237
+
238
+ sleep 10
239
+
240
+ events = get_log_events
241
+ assert_equal(2, events.size)
242
+ assert_equal(time.to_i * 1000, events[0].timestamp)
243
+ assert_equal("{\"cloudwatch\":\"logs1\",\"time\":\"#{time.strftime("%Y-%m-%dT%H:%M:%S%:z")}\"}", events[0].message)
244
+ assert_equal((time.to_i + 1) * 1000, events[1].timestamp)
245
+ assert_equal("{\"cloudwatch\":\"logs2\",\"time\":\"#{(time+1).strftime("%Y-%m-%dT%H:%M:%S%:z")}\"}", events[1].message)
246
+ end
247
+
248
+ def test_log_group_name_key_and_log_stream_name_key
249
+ new_log_stream
250
+
251
+ d = create_driver(<<-EOC)
252
+ #{default_config}
253
+ log_group_name_key group_name_key
254
+ log_stream_name_key stream_name_key
255
+ EOC
256
+
257
+ stream1 = new_log_stream
258
+ stream2 = new_log_stream
259
+
260
+ records = [
261
+ {'cloudwatch' => 'logs1', 'message' => 'message1', 'group_name_key' => log_group_name, 'stream_name_key' => stream1},
262
+ {'cloudwatch' => 'logs2', 'message' => 'message1', 'group_name_key' => log_group_name, 'stream_name_key' => stream2},
263
+ {'cloudwatch' => 'logs3', 'message' => 'message1', 'group_name_key' => log_group_name, 'stream_name_key' => stream1},
264
+ ]
265
+
266
+ time = Time.now
267
+ records.each_with_index do |record, i|
268
+ d.emit(record, time.to_i + i)
269
+ end
270
+ d.run
271
+
272
+ # Call API once for each stream
273
+ assert_equal(2, d.instance.log.logs.select {|l| l =~ /Calling PutLogEvents API/ }.size)
274
+
275
+ sleep 10
276
+
277
+ events = get_log_events(log_group_name, stream1)
278
+ assert_equal(2, events.size)
279
+ assert_equal(time.to_i * 1000, events[0].timestamp)
280
+ assert_equal((time.to_i + 2) * 1000, events[1].timestamp)
281
+ assert_equal(records[0], Yajl.load(events[0].message))
282
+ assert_equal(records[2], Yajl.load(events[1].message))
283
+
284
+ events = get_log_events(log_group_name, stream2)
285
+ assert_equal(1, events.size)
286
+ assert_equal((time.to_i + 1) * 1000, events[0].timestamp)
287
+ assert_equal(records[1], Yajl.load(events[0].message))
288
+ end
289
+
290
+ def test_remove_log_group_name_key_and_remove_log_stream_name_key
291
+ new_log_stream
292
+
293
+ d = create_driver(<<-EOC)
294
+ #{default_config}
295
+ log_group_name_key group_name_key
296
+ log_stream_name_key stream_name_key
297
+ remove_log_group_name_key true
298
+ remove_log_stream_name_key true
299
+ EOC
300
+
301
+ time = Time.now
302
+ d.emit({'cloudwatch' => 'logs1', 'message' => 'message1', 'group_name_key' => log_group_name, 'stream_name_key' => log_stream_name}, time.to_i)
303
+ d.run
304
+
305
+ sleep 10
306
+
307
+ events = get_log_events(log_group_name, log_stream_name)
308
+ assert_equal(1, events.size)
309
+ assert_equal(time.to_i * 1000, events[0].timestamp)
310
+ assert_equal({'cloudwatch' => 'logs1', 'message' => 'message1'}, Yajl.load(events[0].message))
311
+ end
312
+
313
+ def test_retrying_on_throttling_exception
314
+ resp = mock()
315
+ resp.expects(:next_sequence_token)
316
+ client = Aws::CloudWatchLogs::Client.new
317
+ client.stubs(:put_log_events).
318
+ raises(Aws::CloudWatchLogs::Errors::ThrottlingException.new(nil, "error")).then.returns(resp)
319
+
320
+ time = Time.now
321
+ d = create_driver
322
+ d.instance.instance_variable_set(:@logs, client)
323
+ d.emit({'message' => 'message1'}, time.to_i)
324
+ d.run
325
+
326
+ assert_match(/Calling PutLogEvents/, d.instance.log.logs[0])
327
+ assert_match(/failed to PutLogEvents/, d.instance.log.logs[1])
328
+ assert_match(/Calling PutLogEvents/, d.instance.log.logs[2])
329
+ assert_match(/retry succeeded/, d.instance.log.logs[3])
330
+ end
331
+
332
+ def test_retrying_on_throttling_exception_and_throw_away
333
+ client = Aws::CloudWatchLogs::Client.new
334
+ client.stubs(:put_log_events).
335
+ raises(Aws::CloudWatchLogs::Errors::ThrottlingException.new(nil, "error"))
336
+
337
+ time = Time.now
338
+ d = create_driver(<<-EOC)
339
+ #{default_config}
340
+ log_group_name #{log_group_name}
341
+ log_stream_name #{log_stream_name}
342
+ put_log_events_retry_limit 1
343
+ EOC
344
+ d.instance.instance_variable_set(:@logs, client)
345
+ d.emit({'message' => 'message1'}, time.to_i)
346
+ d.run
347
+
348
+ assert_match(/Calling PutLogEvents/, d.instance.log.logs[0])
349
+ assert_match(/failed to PutLogEvents/, d.instance.log.logs[1])
350
+ assert_match(/Calling PutLogEvents/, d.instance.log.logs[2])
351
+ assert_match(/failed to PutLogEvents/, d.instance.log.logs[3])
352
+ assert_match(/Calling PutLogEvents/, d.instance.log.logs[4])
353
+ assert_match(/failed to PutLogEvents and throwing away/, d.instance.log.logs[5])
354
+ end
355
+
356
+ def test_too_large_event
357
+ time = Time.now
358
+ d = create_driver(<<-EOC)
359
+ #{default_config}
360
+ log_group_name #{log_group_name}
361
+ log_stream_name #{log_stream_name}
362
+ EOC
363
+ d.emit({'message' => '*' * 256 * 1024}, time.to_i)
364
+ d.run
365
+
366
+ assert_match(/Log event is discarded because it is too large: 262184 bytes exceeds limit of 262144$/, d.instance.log.logs[0])
367
+ end
368
+
369
+ def test_scrub_record
370
+ record = {
371
+ "hash" => {
372
+ "str" => "\xAE",
373
+ },
374
+ "array" => [
375
+ "\xAE",
376
+ ],
377
+ "str" => "\xAE",
378
+ }
379
+
380
+ d = create_driver
381
+ d.instance.send(:scrub_record!, record)
382
+
383
+ assert_equal("�", record["hash"]["str"])
384
+ assert_equal("�", record["array"][0])
385
+ assert_equal("�", record["str"])
386
+ end
387
+
388
+ private
389
+ def default_config
390
+ <<-EOC
391
+ type cloudwatch_logs
392
+ auto_create_stream true
393
+ #{aws_key_id}
394
+ #{aws_sec_key}
395
+ #{region}
396
+ EOC
397
+ end
398
+
399
+ def create_driver(conf = nil)
400
+ unless conf
401
+ conf = <<-EOC
402
+ #{default_config}
403
+ log_group_name #{log_group_name}
404
+ log_stream_name #{log_stream_name}
405
+ EOC
406
+ end
407
+ Fluent::Test::BufferedOutputTestDriver.new(Fluent::CloudwatchLogsOutput, fluentd_tag).configure(conf)
408
+ end
409
+ end
@@ -0,0 +1,86 @@
1
+ require 'test/unit'
2
+ require 'mocha/test_unit'
3
+ require 'fluent/test'
4
+ require 'securerandom'
5
+
6
+ require 'aws-sdk-cloudwatchlogs'
7
+
8
+ module CloudwatchLogsTestHelper
9
+ private
10
+ def logs
11
+ options = {}
12
+ options[:credentials] = Aws::Credentials.new(ENV['aws_key_id'], ENV['aws_sec_key']) if ENV['aws_key_id'] && ENV['aws_sec_key']
13
+ options[:region] = ENV['region'] if ENV['region']
14
+ options[:http_proxy] = ENV['http_proxy'] if ENV['http_proxy']
15
+ @logs ||= Aws::CloudWatchLogs::Client.new(options)
16
+ end
17
+
18
+ def log_group_name
19
+ @log_group_name ||= "fluent-plugin-cloudwatch-test-#{Time.now.to_f}"
20
+ end
21
+
22
+ def aws_key_id
23
+ "aws_key_id #{ENV['aws_key_id']}" if ENV['aws_key_id']
24
+ end
25
+
26
+ def aws_sec_key
27
+ "aws_sec_key #{ENV['aws_sec_key']}" if ENV['aws_sec_key']
28
+ end
29
+
30
+ def region
31
+ "region #{ENV['region']}" if ENV['region']
32
+ end
33
+
34
+ def log_stream_name(log_stream_name_prefix = nil)
35
+ if !@log_stream_name
36
+ new_log_stream(log_stream_name_prefix)
37
+ end
38
+ @log_stream_name
39
+ end
40
+
41
+ def new_log_stream(log_stream_name_prefix = nil)
42
+ uuid = SecureRandom.uuid
43
+ @log_stream_name = log_stream_name_prefix ? log_stream_name_prefix + uuid : uuid
44
+ end
45
+
46
+ def clear_log_group
47
+ [log_group_name, fluentd_tag].each do |name|
48
+ begin
49
+ logs.delete_log_group(log_group_name: name)
50
+ rescue Aws::CloudWatchLogs::Errors::ResourceNotFoundException
51
+ # pass
52
+ end
53
+ end
54
+ end
55
+
56
+ def fluentd_tag
57
+ @fluentd_tag ||= "fluent.plugin.cloudwatch.test.#{Time.now.to_f}"
58
+ end
59
+
60
+ def create_log_stream()
61
+ begin
62
+ logs.create_log_group(log_group_name: log_group_name)
63
+ rescue Aws::CloudWatchLogs::Errors::ResourceAlreadyExistsException
64
+ # pass
65
+ end
66
+
67
+ begin
68
+ logs.create_log_stream(log_group_name: log_group_name, log_stream_name: log_stream_name)
69
+ rescue Aws::CloudWatchLogs::Errors::ResourceAlreadyExistsException
70
+ # pass
71
+ end
72
+ end
73
+
74
+ def get_log_events(group = log_group_name, stream = log_stream_name)
75
+ logs.get_log_events(log_group_name: group, log_stream_name: stream).events
76
+ end
77
+
78
+ def put_log_events(events)
79
+ args = {
80
+ log_events: events,
81
+ log_group_name: log_group_name,
82
+ log_stream_name: log_stream_name,
83
+ }
84
+ logs.put_log_events(args)
85
+ end
86
+ end