fluent-plugin-feedly 0.0.1
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.
- data/.gitignore +5 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +13 -0
- data/README.md +135 -0
- data/Rakefile +10 -0
- data/fluent-plugin-feedly.gemspec +26 -0
- data/lib/fluent/plugin/in_feedly.rb +148 -0
- data/test/helper.rb +28 -0
- data/test/plugin/test_in_mysql.rb +30 -0
- metadata +122 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright (c) 2012- Kentaro Yoshida
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
# fluent-plugin-feedly
|
2
|
+
|
3
|
+
## Overview
|
4
|
+
|
5
|
+
Fluentd input plugin to fetch RSS/ATOM feed via Feedly Could.
|
6
|
+
|
7
|
+
## Dependencies
|
8
|
+
|
9
|
+
* Ruby 1.9.3+
|
10
|
+
* Fluentd 0.10.54+
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
install with gem or fluent-gem command as:
|
15
|
+
|
16
|
+
`````
|
17
|
+
# for system installed fluentd
|
18
|
+
$ gem install fluent-plugin-feedly
|
19
|
+
|
20
|
+
# for td-agent
|
21
|
+
$ sudo /usr/lib64/fluent/ruby/bin/fluent-gem install fluent-plugin-feedly
|
22
|
+
`````
|
23
|
+
|
24
|
+
## Configuration
|
25
|
+
|
26
|
+
`````
|
27
|
+
<source>
|
28
|
+
type feedly
|
29
|
+
|
30
|
+
# Set feedly access token
|
31
|
+
# ref. https://feedly.com/v3/auth/dev
|
32
|
+
access_token ACCESS_TOKEN # Required
|
33
|
+
|
34
|
+
# Set file-path to store last fetched article position
|
35
|
+
state_file /var/log/td-agent/feedly.state # Required
|
36
|
+
|
37
|
+
# Set output tag
|
38
|
+
tag input.feedly # Required
|
39
|
+
|
40
|
+
# List subscribe categories in your feedly account with JSON Array
|
41
|
+
subscribe_categories ["global.all"] # Optional (default: global.all)
|
42
|
+
|
43
|
+
# Set update checking frequency
|
44
|
+
run_interval 30m # Optional (default: 10m)
|
45
|
+
|
46
|
+
# Set bulk read size
|
47
|
+
fetch_count 20 # Optional (default: 20)
|
48
|
+
|
49
|
+
# fetching range of time within 30d
|
50
|
+
fetch_time_range 3d # Optional (default: 3d)
|
51
|
+
|
52
|
+
# fetching range of time for initial startup within 30d
|
53
|
+
fetch_time_range_on_startup 2w # Optional (default: 2w)
|
54
|
+
|
55
|
+
# Using sandbox account
|
56
|
+
enable_sandbox false # Optional (default: false)
|
57
|
+
|
58
|
+
# Set log level for this plugin. To see debug level, set 'debug' for this value.
|
59
|
+
# it can see at stdout as like `$ tail -f /var/log/td-agent/td-agent.log`
|
60
|
+
log_level info # Optional (default: info)
|
61
|
+
</source>
|
62
|
+
`````
|
63
|
+
|
64
|
+
**note** : The `subscribe_categories` is also supported with single or multi line configuration like below.
|
65
|
+
|
66
|
+
```
|
67
|
+
# single line
|
68
|
+
subscribe_categories ["先端技術", "mysql"]
|
69
|
+
|
70
|
+
# multi line
|
71
|
+
subscribe_categories [
|
72
|
+
"先端技術",
|
73
|
+
"mysql"
|
74
|
+
]
|
75
|
+
```
|
76
|
+
|
77
|
+
## Usage
|
78
|
+
|
79
|
+
After installed this plugin, executing fluentd with following configuration.
|
80
|
+
|
81
|
+
```
|
82
|
+
$ cat /etc/td-agent/td-agent.conf
|
83
|
+
<source>
|
84
|
+
type feedly
|
85
|
+
access_token YOUR_ACCESS_TOKEN
|
86
|
+
state_file /var/log/td-agent/feedly.state
|
87
|
+
tag input.feedly
|
88
|
+
run_interval 30m
|
89
|
+
fetch_time_range 1h
|
90
|
+
fetch_time_range_on_startup 3h
|
91
|
+
log_level debug
|
92
|
+
</source>
|
93
|
+
|
94
|
+
<match input.feedly>
|
95
|
+
type file
|
96
|
+
path /tmp/feedly*.json
|
97
|
+
symlink_path /tmp/feedly.json
|
98
|
+
format json
|
99
|
+
append true
|
100
|
+
</match>
|
101
|
+
```
|
102
|
+
|
103
|
+
You can see the behavior about this plugin with this command.
|
104
|
+
|
105
|
+
```
|
106
|
+
# to check stdout of this plugin
|
107
|
+
$ tail -f /var/log/td-agent/td-agent.log
|
108
|
+
2014-10-16 14:47:01 +0900 [debug]: Feedly: fetched articles. articles=416 request_option={:count=>1000, :continuation=>"148cfb7f516:9371a3c:726280cf", :newerThan=>1412228787000}
|
109
|
+
2014-10-16 15:02:02 +0900 [debug]: Feedly: fetched articles. articles=492 request_option={:count=>1000, :continuation=>nil, :newerThan=>1413428521000}
|
110
|
+
```
|
111
|
+
|
112
|
+
```
|
113
|
+
# to check fetched articles
|
114
|
+
$ tail -f /tmp/feedly.json | jq "."
|
115
|
+
```
|
116
|
+
|
117
|
+
## TODO
|
118
|
+
|
119
|
+
Pull requests are very welcome!!
|
120
|
+
|
121
|
+
## Copyright
|
122
|
+
|
123
|
+
Copyright © 2014- Kentaro Yoshida ([@yoshi_ken](https://twitter.com/yoshi_ken))
|
124
|
+
|
125
|
+
## License
|
126
|
+
|
127
|
+
Apache License, Version 2.0
|
128
|
+
|
129
|
+
## Contributing
|
130
|
+
|
131
|
+
1. Fork it ( https://github.com/[my-github-username]/fluent-plugin-feedly/fork )
|
132
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
133
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
134
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
135
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "fluent-plugin-feedly"
|
7
|
+
#spec.version = Fluent::Plugin::Feedly::VERSION
|
8
|
+
spec.version = "0.0.1"
|
9
|
+
spec.authors = ["Kentaro Yoshida"]
|
10
|
+
spec.email = ["y.ken.studio@gmail.com"]
|
11
|
+
spec.summary = %q{Fluentd input plugin to fetch RSS/ATOM feed via Feedly Could.}
|
12
|
+
#spec.description = %q{TODO: Write a longer description. Optional.}
|
13
|
+
spec.homepage = "https://github.com/y-ken/fluent-plugin-feedly"
|
14
|
+
spec.license = "Apache 2.0"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_runtime_dependency "fluentd"#, "~> 0.10.54"
|
22
|
+
spec.add_runtime_dependency "feedlr"
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
25
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
26
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module Fluent
|
4
|
+
class FeedlyInput < Fluent::Input
|
5
|
+
Plugin.register_input('feedly', self)
|
6
|
+
|
7
|
+
config_param :access_token, :string
|
8
|
+
config_param :state_file, :string
|
9
|
+
config_param :tag, :string
|
10
|
+
|
11
|
+
config_param :subscribe_categories, :array, :default => ['global.all']
|
12
|
+
config_param :run_interval, :time, :default => 60*10 #10m
|
13
|
+
config_param :fetch_count, :integer, :default => 20
|
14
|
+
config_param :fetch_time_range, :time, :default => 60*60*24*3 #3d
|
15
|
+
config_param :fetch_time_range_on_startup, :time, :default => 60*60*24*14 #2w
|
16
|
+
config_param :enable_sandbox, :bool, :default => false
|
17
|
+
|
18
|
+
unless method_defined?(:log)
|
19
|
+
define_method(:log) { $log }
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
require 'feedlr'
|
24
|
+
require 'digest/sha2'
|
25
|
+
|
26
|
+
super
|
27
|
+
end
|
28
|
+
|
29
|
+
def configure(conf)
|
30
|
+
super
|
31
|
+
|
32
|
+
if not @fetch_count >= 20 && @fetch_count <= 10000
|
33
|
+
raise Fluent::ConfigError, "Feedly: fetch_count param (#{@fetch_count}) should be between 20 and 10000."
|
34
|
+
end
|
35
|
+
|
36
|
+
@client = Feedlr::Client.new(
|
37
|
+
oauth_access_token: @access_token,
|
38
|
+
sandbox: @enable_sandbox,
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
def start
|
43
|
+
@profile_id = @client.user_profile.id
|
44
|
+
@state_store = StateStore.new(@state_file)
|
45
|
+
@thread = Thread.new(&method(:run))
|
46
|
+
end
|
47
|
+
|
48
|
+
def shutdown
|
49
|
+
Thread.kill(@thread)
|
50
|
+
end
|
51
|
+
|
52
|
+
def run
|
53
|
+
@initial_loop = true
|
54
|
+
loop do
|
55
|
+
begin
|
56
|
+
fetch
|
57
|
+
rescue => e
|
58
|
+
log.error "Feedly: unexpected error has occoured.", :error => e.message, :error_class => e.class
|
59
|
+
log.error_backtrace e.backtrace
|
60
|
+
sleep @run_interval
|
61
|
+
retry
|
62
|
+
end
|
63
|
+
sleep @run_interval
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def fetch
|
68
|
+
@subscribe_categories.each do |category_name|
|
69
|
+
category_id = "user/#{@profile_id}/category/#{category_name}"
|
70
|
+
fetch_time_range = get_fetch_time_range
|
71
|
+
loop {
|
72
|
+
request_option = { count: @fetch_count, continuation: get_continuation_id, newerThan: fetch_time_range }
|
73
|
+
cursor = @client.stream_entries_contents(category_id, request_option)
|
74
|
+
cursor.items.each do |item|
|
75
|
+
Engine.emit(@tag, Engine.now, item)
|
76
|
+
end
|
77
|
+
log.debug "Feedly: fetched articles.", articles: cursor.items.size, request_option: request_option
|
78
|
+
set_continuation_id(cursor.continuation)
|
79
|
+
break if get_continuation_id.nil?
|
80
|
+
}
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def get_fetch_time_range
|
85
|
+
if @initial_loop
|
86
|
+
@initial_loop = false
|
87
|
+
range = @fetch_time_range_on_startup
|
88
|
+
else
|
89
|
+
range = @fetch_time_range
|
90
|
+
end
|
91
|
+
return (Time.now.to_i - range ) * 1000
|
92
|
+
end
|
93
|
+
|
94
|
+
def subscribe_categories_hash
|
95
|
+
Digest::SHA512.digest(@subscribe_categories.sort.join(''))
|
96
|
+
end
|
97
|
+
|
98
|
+
def set_continuation_id(continuation_id)
|
99
|
+
@state_store.set("continuation", {
|
100
|
+
id: continuation_id,
|
101
|
+
subscribe_categories_hash: subscribe_categories_hash
|
102
|
+
})
|
103
|
+
@state_store.update!
|
104
|
+
end
|
105
|
+
|
106
|
+
def get_continuation_id
|
107
|
+
record = @state_store.get('continuation')
|
108
|
+
if subscribe_categories_hash == record[:subscribe_categories_hash]
|
109
|
+
return record[:id]
|
110
|
+
else
|
111
|
+
return nil
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# implementation has copied from its code.
|
116
|
+
# https://github.com/fluent/fluent-plugin-sql/blob/master/lib/fluent/plugin/in_sql.rb
|
117
|
+
class StateStore
|
118
|
+
def initialize(path)
|
119
|
+
@path = path
|
120
|
+
if File.exists?(@path)
|
121
|
+
@data = YAML.load_file(@path)
|
122
|
+
if @data == false || @data == []
|
123
|
+
# this happens if an users created an empty file accidentally
|
124
|
+
@data = {}
|
125
|
+
elsif !@data.is_a?(Hash)
|
126
|
+
raise "state_file on #{@path.inspect} is invalid"
|
127
|
+
end
|
128
|
+
else
|
129
|
+
@data = {}
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def set(key, data)
|
134
|
+
@data.store(key.to_sym, data)
|
135
|
+
end
|
136
|
+
|
137
|
+
def get(key)
|
138
|
+
@data[key.to_sym] ||= {}
|
139
|
+
end
|
140
|
+
|
141
|
+
def update!
|
142
|
+
File.open(@path, 'w') {|f|
|
143
|
+
f.write YAML.dump(@data)
|
144
|
+
}
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
10
|
+
require 'test/unit'
|
11
|
+
|
12
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
13
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
14
|
+
require 'fluent/test'
|
15
|
+
unless ENV.has_key?('VERBOSE')
|
16
|
+
nulllogger = Object.new
|
17
|
+
nulllogger.instance_eval {|obj|
|
18
|
+
def method_missing(method, *args)
|
19
|
+
# pass
|
20
|
+
end
|
21
|
+
}
|
22
|
+
$log = nulllogger
|
23
|
+
end
|
24
|
+
|
25
|
+
require 'fluent/plugin/in_feedly'
|
26
|
+
|
27
|
+
class Test::Unit::TestCase
|
28
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class FeedlyInputTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
Fluent::Test.setup
|
6
|
+
end
|
7
|
+
|
8
|
+
CONFIG = %[
|
9
|
+
access_token YOUR_ACCESS_TOKEN
|
10
|
+
state_file /var/log/td-agent/feedly.state
|
11
|
+
tag input.feedly
|
12
|
+
run_interval 30m
|
13
|
+
]
|
14
|
+
|
15
|
+
def create_driver(conf=CONFIG,tag='test')
|
16
|
+
Fluent::Test::OutputTestDriver.new(Fluent::FeedlyInput, tag).configure(conf)
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_configure
|
20
|
+
assert_raise(Fluent::ConfigError) {
|
21
|
+
d = create_driver('')
|
22
|
+
}
|
23
|
+
d = create_driver(CONFIG)
|
24
|
+
assert_equal 'YOUR_ACCESS_TOKEN', d.instance.access_token
|
25
|
+
assert_equal '/var/log/td-agent/feedly.state', d.instance.state_file
|
26
|
+
assert_equal 1800, d.instance.run_interval
|
27
|
+
assert_equal 'input.feedly', d.instance.tag
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
metadata
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fluent-plugin-feedly
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Kentaro Yoshida
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-10-16 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: fluentd
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: feedlr
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: bundler
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '1.7'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.7'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rake
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '10.0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '10.0'
|
78
|
+
description:
|
79
|
+
email:
|
80
|
+
- y.ken.studio@gmail.com
|
81
|
+
executables: []
|
82
|
+
extensions: []
|
83
|
+
extra_rdoc_files: []
|
84
|
+
files:
|
85
|
+
- .gitignore
|
86
|
+
- .travis.yml
|
87
|
+
- Gemfile
|
88
|
+
- LICENSE.txt
|
89
|
+
- README.md
|
90
|
+
- Rakefile
|
91
|
+
- fluent-plugin-feedly.gemspec
|
92
|
+
- lib/fluent/plugin/in_feedly.rb
|
93
|
+
- test/helper.rb
|
94
|
+
- test/plugin/test_in_mysql.rb
|
95
|
+
homepage: https://github.com/y-ken/fluent-plugin-feedly
|
96
|
+
licenses:
|
97
|
+
- Apache 2.0
|
98
|
+
post_install_message:
|
99
|
+
rdoc_options: []
|
100
|
+
require_paths:
|
101
|
+
- lib
|
102
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
103
|
+
none: false
|
104
|
+
requirements:
|
105
|
+
- - ! '>='
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: '0'
|
108
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
109
|
+
none: false
|
110
|
+
requirements:
|
111
|
+
- - ! '>='
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '0'
|
114
|
+
requirements: []
|
115
|
+
rubyforge_project:
|
116
|
+
rubygems_version: 1.8.23
|
117
|
+
signing_key:
|
118
|
+
specification_version: 3
|
119
|
+
summary: Fluentd input plugin to fetch RSS/ATOM feed via Feedly Could.
|
120
|
+
test_files:
|
121
|
+
- test/helper.rb
|
122
|
+
- test/plugin/test_in_mysql.rb
|