fluent-plugin-redis-list-source 1.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b6204a0bcfde45f928e8b4366c8a87af214ca5ea8529d6b9d95eb8cdb354868f
4
+ data.tar.gz: e11b9a3529f1b85e561489e83d8c237837183e70c5b6f5a85d2a0db7a6566c66
5
+ SHA512:
6
+ metadata.gz: 075ea5acb0a40510bf218702d4b5f4801baf40a0a1dcbbe04482a26d749e2a8db80c52c8429200734a162c8e6357291aa1437d087e40a4b6f686629f779a0f6f
7
+ data.tar.gz: aa9a27bb0ae35c099f72902b02fc65f33222dbd8ac6ccbc74645f3bbf483da998432906302733af52a12f4531676ed5ba8d77f5a8b3305437292c25c72d4bfa8
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.gem
data/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # Fluent::Plugin::RedisListPoller
2
+
3
+ This gem will help you to connect redis and fluentd. With it you'll be able to get your data from redis with fluentd.
4
+
5
+ ## Requirements
6
+
7
+ * Fluentd v0.14+
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ gem install redis-list-source-fluentd
13
+ ```
14
+
15
+ ## Configuration Options
16
+
17
+ ```
18
+ <source>
19
+ @type redis_listener
20
+ host 127.0.0.1
21
+ port 6379
22
+ password nil
23
+ db 0
24
+ timeout 5.0
25
+ driver ruby
26
+
27
+ key redis_list_item
28
+ command lpop
29
+ batch_size 100
30
+
31
+ tag redis.data
32
+
33
+ poll_interval 0.01
34
+ sleep_interval 5
35
+ retry_interval 5
36
+
37
+ <parse>
38
+ @type json
39
+ </parse>
40
+ </source>
41
+ ```
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = 'fluent-plugin-redis-list-source'
5
+ spec.version = '1.0'
6
+ spec.authors = ['Nikita Kazeichev']
7
+ spec.email = ['kazeichev@yandex.ru']
8
+
9
+ spec.summary = 'Plugin wich help you to transfer data from redis to fluentd'
10
+ spec.description = 'This gem will help you to connect redis and fluentd. With it you\'ll be able to get your data from redis with fluentd.'
11
+ spec.homepage = 'https://github.com/kazeichev/fluent-plugin-redis_list_poller'
12
+ spec.license = '0BSD'
13
+
14
+ spec.files = `git ls-files -z`
15
+ .split("\x0")
16
+ .reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.executables = []
18
+ spec.require_paths = ['lib']
19
+
20
+ spec.add_development_dependency 'bundler'
21
+ spec.add_development_dependency 'rake'
22
+
23
+ spec.add_runtime_dependency 'redis', ['>= 3.3.0', '< 3.4.0']
24
+ spec.add_runtime_dependency 'fluentd', ['>= 0.14.0', '< 2']
25
+ end
@@ -0,0 +1,133 @@
1
+ require "fluent/plugin/input"
2
+ require "fluent/plugin_mixin/redis"
3
+
4
+ module Fluent
5
+ module Plugin
6
+ # Input plugin which will monitor the size of a redis list and periodically
7
+ # output metrics to the login pipeline.
8
+ # @since 0.1.0
9
+ class RedisListMonitorInput < Input
10
+ Plugin.register_input('redis_list_monitor', self)
11
+
12
+ include Fluent::PluginMixin::Redis
13
+ helpers :timer
14
+
15
+ # input plugin parameters
16
+ config_param :tag, :string, :default => nil
17
+
18
+ # Initialize new input plugin
19
+ # @since 0.1.0
20
+ # @return [NilClass]
21
+ def initialize
22
+ super
23
+ end
24
+
25
+ # Initialize attributes and parameters
26
+ # @since 0.1.0
27
+ # @return [NilClass]
28
+ def configure(config)
29
+ super
30
+
31
+ configure_params(config)
32
+ configure_locking(config)
33
+
34
+ @queue_length = 0
35
+ @retry_at = nil
36
+ end
37
+
38
+ # Configure plugin parameters
39
+ # @since 0.1.0
40
+ # @return [NilClass]
41
+ def configure_params(config)
42
+ %w(host port key tag).each do |key|
43
+ next if instance_variable_get("@#{key}")
44
+ raise Fluent::ConfigError, "configuration key missing: #{key}"
45
+ end
46
+ end
47
+
48
+ # Configure locking
49
+ # @since 0.1.0
50
+ # @return [NilClass]
51
+ def configure_locking(config)
52
+ @storage = storage_create(type: 'local')
53
+ @lock_key = "redis:#{@key}:lock"
54
+ end
55
+
56
+ # Prepare the plugin event loop
57
+ #
58
+ # This method will initialize the Redis connection object, create any required Redis structures as well
59
+ # as define and begin the event pollers.
60
+ #
61
+ # @since 0.1.0
62
+ # @return [NilClass]
63
+ def start
64
+ super
65
+
66
+ start_redis
67
+ start_poller
68
+ end
69
+
70
+ def start_poller
71
+ timer_execute(:poll, @poll_interval) do
72
+ action_poll
73
+ end
74
+ end
75
+
76
+ # Tear down the plugin
77
+ # @since 0.1.0
78
+ # @return [NilClass]
79
+ def shutdown
80
+ super
81
+ shutdown_redis
82
+ end
83
+
84
+ # Wether the poller has been temporarily disabled or should fetch messages
85
+ # been temporarily disabled
86
+ # @since 0.1.0
87
+ # @return [TrueClass, FalseClass]
88
+ def sleeping?
89
+ @retry_at and @retry_at >= Engine.now
90
+ end
91
+
92
+ # Set a sleep delay, ensuring that we will not attempt to fetch messages
93
+ # @since 0.1.0
94
+ # @param [Integer] delay, the amount of seconds to wait
95
+ # @return [Integer] timestamp when this expires
96
+ def sleep!(delay = @sleep_interval)
97
+ @retry_at = Engine.now + delay
98
+ end
99
+
100
+ # Action to execute when the monitor event watcher executes
101
+ #
102
+ # The monitor is simply responsible for outputting the queue length to
103
+ # the logs as well as detecting zero length lists.
104
+ #
105
+ # @since 0.1.0
106
+ # @return [NilClass]
107
+ def action_poll
108
+ now = Engine.now
109
+
110
+ if sleeping?
111
+ log.trace "redis worker is sleeping"
112
+ return
113
+ end
114
+
115
+ list_size = @redis.llen(@key)
116
+
117
+ event = {
118
+ "timestamp" => now,
119
+ "message" => "redis queue monitor",
120
+ "hostname" => @host,
121
+ "key" => @key,
122
+ "size" => list_size
123
+ }
124
+
125
+ router.emit @tag, now, event
126
+ rescue => e
127
+ log.error "error monitoring queue", :error => e
128
+ log.error_backtrace
129
+ sleep!(@retry_interval)
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,237 @@
1
+ require "fluent/plugin/input"
2
+ require "fluent/plugin/parser"
3
+ require 'fluent/process'
4
+ require "fluent/plugin_mixin/redis"
5
+
6
+ module Fluent
7
+ module Plugin
8
+ class RedisListPollerInput < Input
9
+ include Fluent::PluginMixin::Redis
10
+
11
+ Plugin.register_input('redis-list-source', self)
12
+ helpers :storage
13
+ helpers :timer
14
+
15
+ # redis list details
16
+ # - command: redis command to execute when fetching messages
17
+ # - batch_size: if greater than 0, fetch messages in batches
18
+ config_param :command, :string, :default => "lpop"
19
+ config_param :batch_size, :integer, :default => 0
20
+
21
+ # input plugin parameters
22
+ config_param :tag, :string, :default => nil
23
+
24
+ # parser plugin parameters
25
+ config_section :parse, :init => true, :multi => false do
26
+ config_set_default :@type, "json"
27
+ end
28
+
29
+ # Initialize new input plugin
30
+ # @since 0.1.0
31
+ # @return [NilClass]
32
+ def initialize
33
+ super
34
+ require 'msgpack'
35
+ end
36
+
37
+ # Initialize attributes and parameters
38
+ # @since 0.1.0
39
+ # @return [NilClass]
40
+ def configure(config)
41
+ super
42
+
43
+ configure_params(config)
44
+ configure_parser(config)
45
+ configure_locking(config)
46
+
47
+ @retry_at = nil
48
+ end
49
+
50
+ # Configure plugin parameters
51
+ # @since 0.1.0
52
+ # @return [NilClass]
53
+ def configure_params(config)
54
+ %w(host port key command tag).each do |key|
55
+ next if instance_variable_get("@#{key}")
56
+ raise Fluent::ConfigError, "configuration key missing: #{key}"
57
+ end
58
+
59
+ unless %w(lpop rpop).include?(@command)
60
+ raise Fluent::ConfigError, "command must be either lpop or rpop"
61
+ end
62
+ end
63
+
64
+ # Configure record parser
65
+ # @since 0.1.0
66
+ # @return [NilClass]
67
+ def configure_parser(config)
68
+ parser_config = @parse.corresponding_config_element
69
+ parser_type = parser_config['@type']
70
+ @parser = Fluent::Plugin.new_parser(parser_type, :parent => self)
71
+ @parser.configure(parser_config)
72
+ end
73
+
74
+ # Configure locking
75
+ # @since 0.1.0
76
+ # @return [NilClass]
77
+ def configure_locking(config)
78
+ @storage = storage_create(type: 'local')
79
+ @lock_key = "fluentd:#{@key}:lock"
80
+ end
81
+
82
+ # Prepare the plugin event loop
83
+ #
84
+ # This method will initialize the Redis connection object, create any required Redis structures as well
85
+ # as define and begin the event pollers.
86
+ #
87
+ # @since 0.1.0
88
+ # @return [NilClass]
89
+ def start
90
+ super
91
+ start_redis
92
+ start_poller
93
+ start_monitor
94
+ end
95
+
96
+ # Prepare the Redis queue poller
97
+ #
98
+ # This timed event will routinely poll items from the Redis list and
99
+ # emit those through the pipeline.
100
+ #
101
+ # @since 0.1.0
102
+ # @return [NilClass]
103
+ def start_poller
104
+ timer_execute(:poller, @poll_interval) do
105
+ action_poll
106
+ end
107
+ end
108
+
109
+ # Prepare the Redis queue monitor
110
+ #
111
+ # This timed event will routinely poll for a lock key and disable the
112
+ # queue poller if required
113
+ #
114
+ # @since 0.1.1
115
+ # @return [NilClass]
116
+ def start_monitor
117
+ timer_execute(:monitor, 1) do
118
+ action_locking_monitor
119
+ end
120
+ end
121
+
122
+ # Tear down the plugin
123
+ # @since 0.1.0
124
+ # @return [NilClass]
125
+ def shutdown
126
+ super
127
+ shutdown_redis
128
+ end
129
+
130
+ # Whether to fetch a single item or a multiple items in batch
131
+ # @since 0.1.0
132
+ # @return [TrueClass, FalseClass]
133
+ def batched?
134
+ @batch_size and @batch_size > 1
135
+ end
136
+
137
+ # Wether the poller has been temporarily disabled or should fetch messages
138
+ # been temporarily disabled
139
+ # @since 0.1.0
140
+ # @return [TrueClass, FalseClass]
141
+ def sleeping?
142
+ @retry_at and @retry_at >= Engine.now
143
+ end
144
+
145
+ # Whether the poller has been locked
146
+ # @since 0.1.0
147
+ # @return [TrueClass, FalseClass]
148
+ def locked?
149
+ @storage.get(@lock_key)
150
+ end
151
+
152
+ # Set a sleep delay, ensuring that we will not attempt to fetch messages
153
+ # @since 0.1.0
154
+ # @param [Integer] delay, the amount of seconds to wait
155
+ # @return [Integer] timestamp when this expires
156
+ def sleep!(delay = @sleep_interval)
157
+ @retry_at = Engine.now + delay
158
+ end
159
+
160
+ # Poll messages from the redis server in either single message or
161
+ # batch mode.
162
+ # @since 0.1.0
163
+ # @param [&block] the block to yield single messages to
164
+ # @return [NilClass]
165
+ def poll_messages
166
+ commands = []
167
+
168
+ if batched?
169
+ @redis.pipelined do
170
+ @batch_size.times do
171
+ commands << @redis.call(@command, @key)
172
+ end
173
+ end
174
+ else
175
+ commands << @redis.call(@command, @key)
176
+ end
177
+
178
+ commands.each do |command|
179
+ yield command.is_a?(Redis::Future) ? command.value : command
180
+ end
181
+ end
182
+
183
+ # Action to execute when polling for the lock key
184
+ # @since 0.1.0
185
+ # @return [NilClass]
186
+ def action_locking_monitor
187
+ lock_value = @redis.get(@lock_key)
188
+ @storage.put(@lock_key, lock_value)
189
+ end
190
+
191
+ # Action to execute when the poller event watcher executes
192
+ #
193
+ # Given that the watcher is pretty lightweight, we simply return if the
194
+ # worker has been set to sleep instead of actually sleeping. Doing
195
+ # otherwise seemed to cause locking.
196
+ #
197
+ # Otherwise we iterate through messages, parse and emit them.
198
+ #
199
+ # @since 0.1.0
200
+ # @return [NilClass]
201
+ def action_poll
202
+ now = Engine.now
203
+ messages = []
204
+
205
+ if sleeping?
206
+ log.trace "redis worker is sleeping"
207
+ return
208
+ end
209
+
210
+ if locked?
211
+ log.trace "redis queue is locked"
212
+ return
213
+ end
214
+
215
+ poll_messages do |message|
216
+ if message.nil?
217
+ log.debug "redis queue is empty"
218
+ sleep!(@sleep_interval)
219
+ break
220
+ end
221
+
222
+ @parser.parse(message) do |time, record|
223
+ if time && record
224
+ router.emit @tag || @key, time || Engine.now, record
225
+ else
226
+ log.warn "failed to parse message: #{message}"
227
+ end
228
+ end
229
+ end
230
+ rescue => e
231
+ log.error "error fetching record", :error => e
232
+ log.error_backtrace
233
+ sleep!(@retry_interval)
234
+ end
235
+ end
236
+ end
237
+ end
@@ -0,0 +1,66 @@
1
+
2
+ module Fluent
3
+ module PluginMixin
4
+ module Redis
5
+ def self.included(base)
6
+ base.class_eval do
7
+ # redis connection details
8
+ config_param :host, :string, :default => '127.0.0.1'
9
+ config_param :port, :integer, :default => 6379
10
+ config_param :path, :string, :default => nil
11
+ config_param :password, :string, :default => nil
12
+ config_param :db, :integer, :default => 0
13
+ config_param :timeout, :float, :default => 5.0
14
+ config_param :driver, :string, :default => "ruby"
15
+
16
+ # redis list details
17
+ # - key: redis key of type `list` to fetch messages from
18
+ # - command: redis command to execute when fetching messages
19
+ # - batch_size: if greater than 0, fetch messages in batches
20
+ config_param :key, :string, :default => nil
21
+
22
+ # worker parameters
23
+ # - poll_inteval: interval between message polling actions
24
+ # *NOTE*: Apparently this must be greather than 0
25
+ # - sleep_interval: interval to wait after receiving 0 messages
26
+ # - retry_interval: interval to wait before retrying after an error
27
+ config_param :poll_interval, :float, :default => 1
28
+ config_param :sleep_interval, :float, :default => 5
29
+ config_param :retry_interval, :float, :default => 5
30
+
31
+ attr_reader :redis
32
+ end
33
+ end
34
+
35
+ # Initialize new input plugin
36
+ # @since 0.1.0
37
+ # @return [NilClass]
38
+ def initialize
39
+ require 'redis'
40
+ super
41
+ end
42
+
43
+ # Prepare the Redis conncection object
44
+ # @since 0.1.0
45
+ # @return [Redis]
46
+ def start_redis
47
+ @redis = ::Redis.new(
48
+ :host => @host,
49
+ :port => @port,
50
+ :db => @db,
51
+ :driver => @driver,
52
+ :timeout => @timeout,
53
+ :password => @password,
54
+ :thread_safe => true
55
+ )
56
+ end
57
+
58
+ # Destroy the Redis connection object
59
+ # @since 0.1.0
60
+ # @return [NilClass]
61
+ def shutdown_redis
62
+ @redis.quit
63
+ end
64
+ end
65
+ end
66
+ end
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fluent-plugin-redis-list-source
3
+ version: !ruby/object:Gem::Version
4
+ version: '1.0'
5
+ platform: ruby
6
+ authors:
7
+ - Nikita Kazeichev
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-05-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: redis
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 3.3.0
48
+ - - "<"
49
+ - !ruby/object:Gem::Version
50
+ version: 3.4.0
51
+ type: :runtime
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: 3.3.0
58
+ - - "<"
59
+ - !ruby/object:Gem::Version
60
+ version: 3.4.0
61
+ - !ruby/object:Gem::Dependency
62
+ name: fluentd
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: 0.14.0
68
+ - - "<"
69
+ - !ruby/object:Gem::Version
70
+ version: '2'
71
+ type: :runtime
72
+ prerelease: false
73
+ version_requirements: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: 0.14.0
78
+ - - "<"
79
+ - !ruby/object:Gem::Version
80
+ version: '2'
81
+ description: This gem will help you to connect redis and fluentd. With it you'll be
82
+ able to get your data from redis with fluentd.
83
+ email:
84
+ - kazeichev@yandex.ru
85
+ executables: []
86
+ extensions: []
87
+ extra_rdoc_files: []
88
+ files:
89
+ - ".gitignore"
90
+ - README.md
91
+ - fluent-plugin-redis-list-source.gemspec
92
+ - lib/fluent/plugin/in_redis_list_monitor.rb
93
+ - lib/fluent/plugin/in_redis_list_poller.rb
94
+ - lib/fluent/plugin_mixin/redis.rb
95
+ homepage: https://github.com/kazeichev/fluent-plugin-redis_list_poller
96
+ licenses:
97
+ - 0BSD
98
+ metadata: {}
99
+ post_install_message:
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ requirements: []
114
+ rubyforge_project:
115
+ rubygems_version: 2.7.6
116
+ signing_key:
117
+ specification_version: 4
118
+ summary: Plugin wich help you to transfer data from redis to fluentd
119
+ test_files: []