fluent-plugin-cloudwatch-logs 0.7.3 → 0.7.4

Sign up to get free protection for your applications and to get access to all the features.
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