fluent-plugin-cloudwatch-logs-yajl 0.4.5

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,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