fluent-plugin-cloudwatch-logs 0.7.3 → 0.7.4

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