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.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +266 -0
- data/Rakefile +9 -0
- data/example/fluentd.conf +23 -0
- data/fluent-plugin-cloudwatch-logs.gemspec +29 -0
- data/lib/fluent/plugin/cloudwatch/logs.rb +11 -0
- data/lib/fluent/plugin/cloudwatch/logs/version.rb +9 -0
- data/lib/fluent/plugin/in_cloudwatch_logs.rb +164 -0
- data/lib/fluent/plugin/out_cloudwatch_logs.rb +377 -0
- data/test/plugin/test_in_cloudwatch_logs.rb +157 -0
- data/test/plugin/test_out_cloudwatch_logs.rb +409 -0
- data/test/test_helper.rb +86 -0
- metadata +173 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1a9ff9da35695288dfdbeca3ba8ed98a7a7ff2cd
|
4
|
+
data.tar.gz: 0d2dc63be5193df07e3dcbf91c229dbbf8797428
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4e48ea1183b4d9bbc2e491cce9e6ef9ccf5133099851182c637cb69573d033bae47fa06daded92b196fa67867a1289fa18b103c674097ffc900c32b63e2be22d
|
7
|
+
data.tar.gz: 570be1022decbab4f8f9ea124e149060f78aeca0f7caf1f7e546c1a7ecb77bd31bd31914ac824dbcee565a5a0c6e6e421f52f698d20fb085f0add272dd6ad4a2
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Ryota Arai
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,266 @@
|
|
1
|
+
# fluent-plugin-cloudwatch-logs
|
2
|
+
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/fluent-plugin-cloudwatch-logs.svg)](http://badge.fury.io/rb/fluent-plugin-cloudwatch-logs)
|
4
|
+
|
5
|
+
[CloudWatch Logs](http://aws.amazon.com/blogs/aws/cloudwatch-log-service/) Plugin for Fluentd
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
$ gem install fluent-plugin-cloudwatch-logs
|
10
|
+
|
11
|
+
## Preparation
|
12
|
+
|
13
|
+
Create IAM user with a policy like the following:
|
14
|
+
|
15
|
+
```json
|
16
|
+
{
|
17
|
+
"Version": "2012-10-17",
|
18
|
+
"Statement": [
|
19
|
+
{
|
20
|
+
"Effect": "Allow",
|
21
|
+
"Action": [
|
22
|
+
"logs:*",
|
23
|
+
"s3:GetObject"
|
24
|
+
],
|
25
|
+
"Resource": [
|
26
|
+
"arn:aws:logs:us-east-1:*:*",
|
27
|
+
"arn:aws:s3:::*"
|
28
|
+
]
|
29
|
+
}
|
30
|
+
]
|
31
|
+
}
|
32
|
+
```
|
33
|
+
|
34
|
+
Set region and credentials:
|
35
|
+
|
36
|
+
```
|
37
|
+
$ export AWS_REGION=us-east-1
|
38
|
+
$ export AWS_ACCESS_KEY_ID="YOUR_ACCESS_KEY"
|
39
|
+
$ export AWS_SECRET_ACCESS_KEY="YOUR_SECRET_ACCESS_KEY"
|
40
|
+
```
|
41
|
+
|
42
|
+
## Example
|
43
|
+
|
44
|
+
Start fluentd:
|
45
|
+
|
46
|
+
```
|
47
|
+
$ fluentd -c example/fluentd.conf
|
48
|
+
```
|
49
|
+
|
50
|
+
Send sample log to CloudWatch Logs:
|
51
|
+
|
52
|
+
```
|
53
|
+
$ echo '{"hello":"world"}' | fluent-cat test.cloudwatch_logs.out
|
54
|
+
```
|
55
|
+
|
56
|
+
Fetch sample log from CloudWatch Logs:
|
57
|
+
|
58
|
+
```
|
59
|
+
# stdout
|
60
|
+
2014-07-17 00:28:02 +0900 test.cloudwatch_logs.in: {"hello":"world"}
|
61
|
+
```
|
62
|
+
|
63
|
+
## Configuration
|
64
|
+
### out_cloudwatch_logs
|
65
|
+
|
66
|
+
```
|
67
|
+
<match tag>
|
68
|
+
type cloudwatch_logs
|
69
|
+
log_group_name log-group-name
|
70
|
+
log_stream_name log-stream-name
|
71
|
+
auto_create_stream true
|
72
|
+
#message_keys key1,key2,key3,...
|
73
|
+
#max_message_length 32768
|
74
|
+
#use_tag_as_group false
|
75
|
+
#use_tag_as_stream false
|
76
|
+
#include_time_key true
|
77
|
+
#localtime true
|
78
|
+
#log_group_name_key group_name_key
|
79
|
+
#log_stream_name_key stream_name_key
|
80
|
+
#remove_log_group_name_key true
|
81
|
+
#remove_log_stream_name_key true
|
82
|
+
#put_log_events_retry_wait 1s
|
83
|
+
#put_log_events_retry_limit 17
|
84
|
+
#put_log_events_disable_retry_limit false
|
85
|
+
</match>
|
86
|
+
```
|
87
|
+
|
88
|
+
* `log_group_name`: name of log group to store logs
|
89
|
+
* `log_stream_name`: name of log stream to store logs
|
90
|
+
* `auto_create_stream`: to create log group and stream automatically
|
91
|
+
* `message_keys`: keys to send messages as events
|
92
|
+
* `max_message_length`: maximum length of the message
|
93
|
+
* `max_events_per_batch`: maximum number of events to send at once (default 10000)
|
94
|
+
* `use_tag_as_group`: to use tag as a group name
|
95
|
+
* `use_tag_as_stream`: to use tag as a stream name
|
96
|
+
* `include_time_key`: include time key as part of the log entry (defaults to UTC)
|
97
|
+
* `localtime`: use localtime timezone for `include_time_key` output (overrides UTC default)
|
98
|
+
* `log_group_name_key`: use specified field of records as log group name
|
99
|
+
* `log_stream_name_key`: use specified field of records as log stream name
|
100
|
+
* `remove_log_group_name_key`: remove field specified by `log_group_name_key`
|
101
|
+
* `remove_log_stream_name_key`: remove field specified by `log_stream_name_key`
|
102
|
+
* `put_log_events_retry_wait`: time before retrying PutLogEvents (retry interval increases exponentially like `put_log_events_retry_wait * (2 ^ retry_count)`)
|
103
|
+
* `put_log_events_retry_limit`: maximum count of retry (if exceeding this, the events will be discarded)
|
104
|
+
* `put_log_events_disable_retry_limit`: if true, `put_log_events_retry_limit` will be ignored
|
105
|
+
|
106
|
+
### in_cloudwatch_logs
|
107
|
+
|
108
|
+
```
|
109
|
+
<source>
|
110
|
+
type cloudwatch_logs
|
111
|
+
tag cloudwatch.in
|
112
|
+
log_group_name group
|
113
|
+
log_stream_name stream
|
114
|
+
#use_log_stream_name_prefix true
|
115
|
+
state_file /var/lib/fluent/group_stream.in.state
|
116
|
+
</source>
|
117
|
+
```
|
118
|
+
|
119
|
+
* `tag`: fluentd tag
|
120
|
+
* `log_group_name`: name of log group to fetch logs
|
121
|
+
* `log_stream_name`: name of log stream to fetch logs
|
122
|
+
* `use_log_stream_name_prefix`: to use `log_stream_name` as log stream name prefix (default false)
|
123
|
+
* `state_file`: file to store current state (e.g. next\_forward\_token)
|
124
|
+
* `aws_use_sts`: use [AssumeRoleCredentials](http://docs.aws.amazon.com/sdkforruby/api/Aws/AssumeRoleCredentials.html) to authenticate, rather than the [default credential hierarchy](http://docs.aws.amazon.com/sdkforruby/api/Aws/CloudWatchLogs/Client.html#initialize-instance_method). See 'Cross-Account Operation' below for more detail.
|
125
|
+
* `aws_sts_role_arn`: the role ARN to assume when using cross-account sts authentication
|
126
|
+
* `aws_sts_session_name`: the session name to use with sts authentication (default: `fluentd`)
|
127
|
+
|
128
|
+
This plugin uses [fluent-mixin-config-placeholders](https://github.com/tagomoris/fluent-mixin-config-placeholders) and you can use addtional variables such as %{hostname}, %{uuid}, etc. These variables are useful to put hostname in `log_stream_name`.
|
129
|
+
|
130
|
+
## Test
|
131
|
+
|
132
|
+
Set credentials:
|
133
|
+
|
134
|
+
```
|
135
|
+
$ export AWS_REGION=us-east-1
|
136
|
+
$ export AWS_ACCESS_KEY_ID="YOUR_ACCESS_KEY"
|
137
|
+
$ export AWS_SECRET_ACCESS_KEY="YOUR_SECRET_KEY"
|
138
|
+
```
|
139
|
+
|
140
|
+
Run tests:
|
141
|
+
|
142
|
+
```
|
143
|
+
$ rake test
|
144
|
+
```
|
145
|
+
|
146
|
+
Or, If you do not want to use IAM roll or ENV(this is just like writing to configuration file) :
|
147
|
+
|
148
|
+
```
|
149
|
+
$ rake aws_key_id=YOUR_ACCESS_KEY aws_sec_key=YOUR_SECRET_KEY region=us-east-1 test
|
150
|
+
```
|
151
|
+
|
152
|
+
## Caution
|
153
|
+
|
154
|
+
- If an event message exceeds API limit (256KB), the event will be discarded.
|
155
|
+
|
156
|
+
## Cross-Account Operation
|
157
|
+
In order to have an instance of this plugin running in one AWS account to fetch logs from another account cross-account IAM authentication is required. Whilst this can be accomplished by configuring specific instances of the plugin manually with credentials for the source account in question this is not desirable for a number of reasons.
|
158
|
+
|
159
|
+
In this case IAM can be used to allow the fluentd instance in one account ("A") to ingest Cloudwatch logs from another ("B") via the following mechanic:
|
160
|
+
|
161
|
+
* plugin instance running in account "A" has an IAM instance role assigned to the underlying EC2 instance
|
162
|
+
* The IAM instance role and associated policies permit the EC2 instance to assume a role in another account
|
163
|
+
* An IAM role in account "B" and associated policies allow read access to the Cloudwatch Logs service, as appropriate.
|
164
|
+
|
165
|
+
### IAM Detail: Consuming Account "A"
|
166
|
+
|
167
|
+
* Create an IAM role `cloudwatch`
|
168
|
+
* Attach a policy to allow the role holder to assume another role (where `ACCOUNT-B` is substituted for the appropriate account number):
|
169
|
+
|
170
|
+
```
|
171
|
+
{
|
172
|
+
"Version": "2012-10-17",
|
173
|
+
"Statement": [
|
174
|
+
{
|
175
|
+
"Effect": "Allow",
|
176
|
+
"Action": [
|
177
|
+
"sts:*"
|
178
|
+
],
|
179
|
+
"Resource": [
|
180
|
+
"arn:aws:iam::ACCOUNT-B:role/fluentd"
|
181
|
+
]
|
182
|
+
}
|
183
|
+
]
|
184
|
+
}
|
185
|
+
```
|
186
|
+
|
187
|
+
* Ensure the EC2 instance on which this plugin is executing as role `cloudwatch` as its assigned IAM instance role.
|
188
|
+
|
189
|
+
### IAM Detail: Log Source Account "B"
|
190
|
+
|
191
|
+
* Create an IAM role `fluentd`
|
192
|
+
* Ensure the `fluentd` role as account "A" as a trusted entity:
|
193
|
+
|
194
|
+
```
|
195
|
+
{
|
196
|
+
"Version": "2012-10-17",
|
197
|
+
"Statement": [
|
198
|
+
{
|
199
|
+
"Effect": "Allow",
|
200
|
+
"Principal": {
|
201
|
+
"AWS": "arn:aws:iam::ACCOUNT-A:root"
|
202
|
+
},
|
203
|
+
"Action": "sts:AssumeRole"
|
204
|
+
}
|
205
|
+
]
|
206
|
+
}
|
207
|
+
```
|
208
|
+
|
209
|
+
* Attach a policy:
|
210
|
+
|
211
|
+
```
|
212
|
+
{
|
213
|
+
"Version": "2012-10-17",
|
214
|
+
"Statement": [
|
215
|
+
{
|
216
|
+
"Effect": "Allow",
|
217
|
+
"Action": [
|
218
|
+
"logs:DescribeDestinations",
|
219
|
+
"logs:DescribeExportTasks",
|
220
|
+
"logs:DescribeLogGroups",
|
221
|
+
"logs:DescribeLogStreams",
|
222
|
+
"logs:DescribeMetricFilters",
|
223
|
+
"logs:DescribeSubscriptionFilters",
|
224
|
+
"logs:FilterLogEvents",
|
225
|
+
"logs:GetLogEvents"
|
226
|
+
],
|
227
|
+
"Resource": [
|
228
|
+
"arn:aws:logs:eu-west-1:ACCOUNT-B:log-group:LOG_GROUP_NAME_FOR_CONSUMPTION:*"
|
229
|
+
]
|
230
|
+
}
|
231
|
+
]
|
232
|
+
}
|
233
|
+
```
|
234
|
+
|
235
|
+
### Configuring the plugin for STS authentication
|
236
|
+
```
|
237
|
+
<source>
|
238
|
+
type cloudwatch_logs
|
239
|
+
region us-east-1 # You must supply a region
|
240
|
+
aws_use_sts true
|
241
|
+
aws_sts_role_arn arn:aws:iam::ACCOUNT-B:role/fluentd
|
242
|
+
log_group_name LOG_GROUP_NAME_FOR_CONSUMPTION
|
243
|
+
log_stream_name SOME_PREFIX
|
244
|
+
use_log_stream_name_prefix true
|
245
|
+
state_file /path/to/state_file
|
246
|
+
format /(?<message>.+)/
|
247
|
+
</source>
|
248
|
+
```
|
249
|
+
|
250
|
+
## TODO
|
251
|
+
|
252
|
+
* out_cloudwatch_logs
|
253
|
+
* if the data is too big for API, split into multiple requests
|
254
|
+
* format
|
255
|
+
* check data size
|
256
|
+
* in_cloudwatch_logs
|
257
|
+
* format
|
258
|
+
* fallback to start_time because next_token expires after 24 hours
|
259
|
+
|
260
|
+
## Contributing
|
261
|
+
|
262
|
+
1. Fork it ( https://github.com/[my-github-username]/fluent-plugin-cloudwatch-logs/fork )
|
263
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
264
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
265
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
266
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
<source>
|
2
|
+
type forward
|
3
|
+
</source>
|
4
|
+
|
5
|
+
<source>
|
6
|
+
type cloudwatch_logs
|
7
|
+
tag test.cloudwatch_logs.in
|
8
|
+
log_group_name fluent-plugin-cloudwatch-example
|
9
|
+
log_stream_name fluent-plugin-cloudwatch-example
|
10
|
+
state_file /tmp/fluent-plugin-cloudwatch-example.state
|
11
|
+
</source>
|
12
|
+
|
13
|
+
<match test.cloudwatch_logs.out>
|
14
|
+
type cloudwatch_logs
|
15
|
+
log_group_name fluent-plugin-cloudwatch-example
|
16
|
+
log_stream_name fluent-plugin-cloudwatch-example
|
17
|
+
auto_create_stream true
|
18
|
+
</match>
|
19
|
+
|
20
|
+
<match test.cloudwatch_logs.in>
|
21
|
+
type stdout
|
22
|
+
</match>
|
23
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'fluent/plugin/cloudwatch/logs/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "fluent-plugin-cloudwatch-logs-yajl"
|
8
|
+
spec.version = Fluent::Plugin::Cloudwatch::Logs::VERSION
|
9
|
+
spec.authors = ["Todd Scott"]
|
10
|
+
spec.email = ["tscott1213@gmail.com"]
|
11
|
+
spec.summary = %q{CloudWatch Logs Plugin for Fluentd}
|
12
|
+
spec.homepage = "https://github.com/tscott1213/fluent-plugin-cloudwatch-logs"
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0")
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_dependency 'fluentd'
|
21
|
+
spec.add_dependency 'yajl-ruby', '~> 1.0'
|
22
|
+
spec.add_dependency 'aws-sdk-cloudwatchlogs', '~> 1.0'
|
23
|
+
spec.add_dependency 'fluent-mixin-config-placeholders', '>= 0.2.0'
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
26
|
+
spec.add_development_dependency "rake"
|
27
|
+
spec.add_development_dependency "test-unit"
|
28
|
+
spec.add_development_dependency "mocha"
|
29
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
require 'fluent/input'
|
2
|
+
require 'fluent/parser'
|
3
|
+
|
4
|
+
module Fluent
|
5
|
+
require 'fluent/mixin/config_placeholders'
|
6
|
+
|
7
|
+
class CloudwatchLogsInput < Input
|
8
|
+
Plugin.register_input('cloudwatch_logs', self)
|
9
|
+
|
10
|
+
include Fluent::Mixin::ConfigPlaceholders
|
11
|
+
|
12
|
+
# Define `router` method of v0.12 to support v0.10.57 or earlier
|
13
|
+
unless method_defined?(:router)
|
14
|
+
define_method("router") { Engine }
|
15
|
+
end
|
16
|
+
|
17
|
+
config_param :aws_key_id, :string, :default => nil, :secret => true
|
18
|
+
config_param :aws_sec_key, :string, :default => nil, :secret => true
|
19
|
+
config_param :aws_use_sts, :bool, default: false
|
20
|
+
config_param :aws_sts_role_arn, :string, default: nil
|
21
|
+
config_param :aws_sts_session_name, :string, default: 'fluentd'
|
22
|
+
config_param :region, :string, :default => nil
|
23
|
+
config_param :tag, :string
|
24
|
+
config_param :log_group_name, :string
|
25
|
+
config_param :log_stream_name, :string
|
26
|
+
config_param :use_log_stream_name_prefix, :bool, default: false
|
27
|
+
config_param :state_file, :string
|
28
|
+
config_param :fetch_interval, :time, default: 60
|
29
|
+
config_param :http_proxy, :string, default: nil
|
30
|
+
|
31
|
+
def initialize
|
32
|
+
super
|
33
|
+
|
34
|
+
require 'aws-sdk-cloudwatchlogs'
|
35
|
+
end
|
36
|
+
|
37
|
+
def placeholders
|
38
|
+
[:percent]
|
39
|
+
end
|
40
|
+
|
41
|
+
def configure(conf)
|
42
|
+
super
|
43
|
+
configure_parser(conf)
|
44
|
+
end
|
45
|
+
|
46
|
+
def start
|
47
|
+
options = {}
|
48
|
+
options[:region] = @region if @region
|
49
|
+
options[:http_proxy] = @http_proxy if @http_proxy
|
50
|
+
|
51
|
+
if @aws_use_sts
|
52
|
+
Aws.config[:region] = options[:region]
|
53
|
+
options[:credentials] = Aws::AssumeRoleCredentials.new(
|
54
|
+
role_arn: @aws_sts_role_arn,
|
55
|
+
role_session_name: @aws_sts_session_name
|
56
|
+
)
|
57
|
+
else
|
58
|
+
options[:credentials] = Aws::Credentials.new(@aws_key_id, @aws_sec_key) if @aws_key_id && @aws_sec_key
|
59
|
+
end
|
60
|
+
|
61
|
+
@logs = Aws::CloudWatchLogs::Client.new(options)
|
62
|
+
|
63
|
+
@finished = false
|
64
|
+
@thread = Thread.new(&method(:run))
|
65
|
+
end
|
66
|
+
|
67
|
+
def shutdown
|
68
|
+
@finished = true
|
69
|
+
@thread.join
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
def configure_parser(conf)
|
74
|
+
if conf['format']
|
75
|
+
@parser = Fluent::TextParser.new
|
76
|
+
@parser.configure(conf)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def state_file_for(log_stream_name)
|
81
|
+
return "#{@state_file}_#{log_stream_name.gsub(File::SEPARATOR, '-')}" if log_stream_name
|
82
|
+
return @state_file
|
83
|
+
end
|
84
|
+
|
85
|
+
def next_token(log_stream_name)
|
86
|
+
return nil unless File.exist?(state_file_for(log_stream_name))
|
87
|
+
File.read(state_file_for(log_stream_name)).chomp
|
88
|
+
end
|
89
|
+
|
90
|
+
def store_next_token(token, log_stream_name = nil)
|
91
|
+
open(state_file_for(log_stream_name), 'w') do |f|
|
92
|
+
f.write token
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def run
|
97
|
+
@next_fetch_time = Time.now
|
98
|
+
|
99
|
+
until @finished
|
100
|
+
if Time.now > @next_fetch_time
|
101
|
+
@next_fetch_time += @fetch_interval
|
102
|
+
|
103
|
+
if @use_log_stream_name_prefix
|
104
|
+
log_streams = describe_log_streams
|
105
|
+
log_streams.each do |log_stream|
|
106
|
+
log_stream_name = log_stream.log_stream_name
|
107
|
+
events = get_events(log_stream_name)
|
108
|
+
events.each do |event|
|
109
|
+
emit(log_stream_name, event)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
else
|
113
|
+
events = get_events(@log_stream_name)
|
114
|
+
events.each do |event|
|
115
|
+
emit(log_stream_name, event)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
sleep 1
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def emit(stream, event)
|
124
|
+
if @parser
|
125
|
+
record = @parser.parse(event.message)
|
126
|
+
router.emit(@tag, record[0], record[1])
|
127
|
+
else
|
128
|
+
time = (event.timestamp / 1000).floor
|
129
|
+
record = Yajl.load(event.message)
|
130
|
+
router.emit(@tag, time, record)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def get_events(log_stream_name)
|
135
|
+
request = {
|
136
|
+
log_group_name: @log_group_name,
|
137
|
+
log_stream_name: log_stream_name
|
138
|
+
}
|
139
|
+
request[:next_token] = next_token(log_stream_name) if next_token(log_stream_name)
|
140
|
+
response = @logs.get_log_events(request)
|
141
|
+
store_next_token(response.next_forward_token, log_stream_name)
|
142
|
+
|
143
|
+
response.events
|
144
|
+
end
|
145
|
+
|
146
|
+
def describe_log_streams(log_streams = nil, next_token = nil)
|
147
|
+
request = {
|
148
|
+
log_group_name: @log_group_name
|
149
|
+
}
|
150
|
+
request[:next_token] = next_token if next_token
|
151
|
+
request[:log_stream_name_prefix] = @log_stream_name
|
152
|
+
response = @logs.describe_log_streams(request)
|
153
|
+
if log_streams
|
154
|
+
log_streams.concat(response.log_streams)
|
155
|
+
else
|
156
|
+
log_streams = response.log_streams
|
157
|
+
end
|
158
|
+
if response.next_token
|
159
|
+
log_streams = describe_log_streams(log_streams, response.next_token)
|
160
|
+
end
|
161
|
+
log_streams
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|