redis-list-source-fluentd 1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d1641d4cbbe1ba29475a656404eb224da18acd4c710ea63e0df8dbff97f3b238
4
+ data.tar.gz: 342a0fad90b2493c092c80d12ccfebe99d37340b14006a42bcb214ebfe967d4a
5
+ SHA512:
6
+ metadata.gz: b3b91f93a3f857b1493f983d43017c6a36c1b63515a9f1650c95c43a5222a3113190d50b62b0786babeabc048e613153ce56698d304f58728714414764c47bb0
7
+ data.tar.gz: 6f00253cfc030868b5c4a579afc411d5244031dc2d02fc64b7703fd1c7b81fd886914434cabc872a49e06e39d767982e67cc38f105fcf215a0ecce61a6209a57
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 fluent-plugin-redis_list_poller
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
+ ```
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "aargh"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -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_listener', 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
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = 'redis-list-source-fluentd'
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
metadata ADDED
@@ -0,0 +1,121 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: redis-list-source-fluentd
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
+ - bin/console
92
+ - bin/setup
93
+ - lib/fluent/plugin/in_redis_list_monitor.rb
94
+ - lib/fluent/plugin/in_redis_list_poller.rb
95
+ - lib/fluent/plugin_mixin/redis.rb
96
+ - redis-to-fluentd-listener.gemspec
97
+ homepage: https://github.com/kazeichev/fluent-plugin-redis_list_poller
98
+ licenses:
99
+ - 0BSD
100
+ metadata: {}
101
+ post_install_message:
102
+ rdoc_options: []
103
+ require_paths:
104
+ - lib
105
+ required_ruby_version: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ required_rubygems_version: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ requirements: []
116
+ rubyforge_project:
117
+ rubygems_version: 2.7.6
118
+ signing_key:
119
+ specification_version: 4
120
+ summary: Plugin wich help you to transfer data from redis to fluentd
121
+ test_files: []