fluent-plugin-ddl-redis-store 0.2.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1f204dfec7c332be1ea6915acb4a008cb957e10bf1686116403fe717fbd535d7
4
+ data.tar.gz: 281b6c3ce41822f415bb1774b3e9e3729cd96604ac15c2838207adbbad7854f6
5
+ SHA512:
6
+ metadata.gz: c7fb2cfb2a447f2a1d90e9b1d60467c5c24f5794c57b115b6cd522d2126ee66c911945eaea79533f023f2f4e67c1d68dd766b7d7bf6588434dc0d0f623575d64
7
+ data.tar.gz: d6fcfc4cb3e89b2602f327a43fb73aca598edcad5e6d9071748dac6455a71b7b2ecf3fbf15bc3c7b52daa0a5578d58668e452920ce8e17d9d955b754ed18d4eb
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ *.gem
data/AUTHORS ADDED
@@ -0,0 +1,2 @@
1
+ moaikids
2
+ HANAI Tohru aka pokehanai
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/README.md ADDED
@@ -0,0 +1,183 @@
1
+ Redis Output Plugin For fluentd
2
+ ===============================
3
+ [Fluentd][] output plugin to upload/publish event data to [Redis][] storage.
4
+
5
+ [Fluentd]: http://fluentd.org/
6
+ [Redis]: http://redis.io/
7
+
8
+ Requirements
9
+ ------------
10
+
11
+ | fluent-plugin-redis-store | fluentd | ruby |
12
+ |------------------------|---------|------|
13
+ | >= 0.2.0 | >= v0.14.15 | >= 2.1 |
14
+ | < 0.2.0 | >= v0.12.0 | >= 1.9 |
15
+
16
+ Background
17
+ ----------
18
+
19
+ This is folked project from [fluent-plugin-redisstore][].
20
+
21
+ [fluent-plugin-redisstore]: https://github.com/moaikids/fluent-plugin-redisstore
22
+
23
+ Features
24
+ --------
25
+
26
+ #### Supported Redis commands
27
+
28
+ Currently the plugin supports following Redis commands:
29
+
30
+ - **set** by `string` type (of the plugin)
31
+ - **lpush**/**rpush** by `list` type
32
+ - **sadd** by `set` type
33
+ - **zadd** by `zset` type
34
+ - **publish** by `publish` type
35
+
36
+ #### Supported _value_ format
37
+
38
+ - plain(as is)
39
+ - JSON
40
+ - [MessagePack](http://msgpack.org/)
41
+
42
+ #### _key_ string for Redis storage
43
+
44
+ Redis commands require _key_ and _value_.
45
+ For _key_, the plugin supports either way;
46
+
47
+ 1. Specify a fixed key.
48
+ You can do this simply using `key` option in td-agent configuration file.
49
+
50
+ ```apache
51
+ type redis_store
52
+ key userdata
53
+ ```
54
+
55
+ 2. Lookup a key string in every event data by a lookup path.
56
+ If event data have structured data like
57
+
58
+ ```javascript
59
+ { "user": { "name": "Kei" } }
60
+ ```
61
+
62
+ and you want to use each name of user, you can use `key_path` option.
63
+
64
+ ```apache
65
+ type redis_store
66
+ key_path user.name
67
+ ```
68
+
69
+ With the above data, `Kei` will be a _key_.
70
+
71
+ In addition, `key_prefix` and `key_suffix` are useful in some cases. Both are available either `key` and `key_path`
72
+
73
+ ```apache
74
+ type redis_store
75
+ key_path user.name
76
+ key_prefix ouruser.
77
+ key_suffix .accesslog
78
+ ```
79
+
80
+ With the previous data, _key_ will be `outuser.Kei.accesslog`.
81
+
82
+ #### _value_ data for Redis storage
83
+
84
+ To determine what _value_ in every event data to be srtored, you have two options;
85
+
86
+ 1. Store extracted data in event data, by a lookup path with `value_path` option.
87
+ It works like `key_path`.
88
+ 2. Store whole data.
89
+ This is default behavior. To do it, simply omit `value_path` option.
90
+
91
+ Installation
92
+ ------------
93
+
94
+ ```bash
95
+ fluent-gem install fluent-plugin-redis-store
96
+
97
+ # or if you are using td-agent:
98
+ td-agent-gem install fluent-plugin-redis-store
99
+ ```
100
+
101
+ Configuration
102
+ -------------
103
+
104
+ ### Redis connection
105
+
106
+ | Key | Type | Required? | Default | Description |
107
+ | :---- | :----- | :---------- | :----------------------- | :------------ |
108
+ | `host` | string | Optional | 127.0.0.1 | host name of Redis server |
109
+ | `port` | int | Optional | 6379 | port number of Redis server |
110
+ | `password` | string | Optional | | password for Redis connection |
111
+ | `path` | string | Optional | | To connect via Unix socket, try '/tmp/redis.sock' |
112
+ | `db` | int | Optional | 0 | DB number of Redis |
113
+ | `timeout` | float | Optional | 5.0 | connection timeout in seconds |
114
+
115
+ ### common options for storages
116
+
117
+ | Key | Type | Default | Description |
118
+ | :---- | :----- | :----------------------- | :------------ |
119
+ | `key` | string | | Fixed _key_ used to store(publish) in Redis |
120
+ | `key_path` | string | | path to lookup for _key_ in the event data |
121
+ | `key_prefix` | string | | prefix of _key_ |
122
+ | `key_suffix` | string | | suffix of _key_ |
123
+ | `value_path` | string | (whole event data) | path to lookup for _value_ in the event data |
124
+ | `store_type` | string | zset | `string`/`list`/`set`/`zset`/`publish` |
125
+ | `format_type` | string | plain | format type for _value_ (`plain`/`json`/`msgpack`) |
126
+ | `key_expire` | int | -1 | If set, the key will be expired in specified seconds |
127
+ | `flush_interval` | time | 1 | Time interval which events will be flushed to Redis |
128
+
129
+ Note: either `key` or `key_path` is required.
130
+
131
+ ### `string` storage specific options
132
+
133
+ | Key | Type | Default | Description |
134
+ | :---- | :----- | :----------------------- | :------------ |
135
+ | `type` | string | | Fixed _key_ used to store(publish) in Redis |
136
+ No more options than common options.
137
+
138
+ ### `list` storage specific options
139
+
140
+ | Key | Type | Default | Description |
141
+ | :---- | :----- | :----------------------- | :------------ |
142
+ | `order` | string | asc | `asc`: **rpush**, `desc`: **lpush** |
143
+
144
+ ### `set` storage specific options
145
+
146
+ No more options than common options.
147
+
148
+ ### `zset` storage specific options
149
+
150
+ | Key | Type | Default | Description |
151
+ | :---- | :----- | :----------------------- | :------------ |
152
+ | `score_path` | string | (_time_ of log event) | path to lookup for _score_ in the event data |
153
+ | `collision_policy` | string | (nil) | Only update elements that already exist (XX) or add a new element (NX) |
154
+ | `value_expire` | int | | value expiration in seconds |
155
+
156
+ If `value_expire` is set, the plugin assumes that the _score_ in the **SortedSet** is
157
+ based on *timestamp* and it deletes expired _members_ every after new event data arrives.
158
+
159
+ ### `publish` storage specific options
160
+
161
+ No more options than common options.
162
+
163
+
164
+ Contributors
165
+ ------------
166
+
167
+ moaikids
168
+ HANAI tohru
169
+ Mohit Khanna
170
+ yamada-shinji
171
+ Heitor de Souza
172
+ Hiroshi Hatake
173
+ Gabriel Bordeaux
174
+
175
+ Copyright
176
+ ---------
177
+
178
+ Copyright (c) 2013 moaikids
179
+ Copyright (c) 2014 HANAI Tohru
180
+
181
+ License
182
+ -------
183
+ Apache License, Version 2.0
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+
4
+ require 'rake/testtask'
5
+ Rake::TestTask.new(:test) do |test|
6
+ test.libs << 'lib' << 'test'
7
+ test.pattern = 'test/**/test_*.rb'
8
+ test.verbose = true
9
+ end
10
+
11
+ task :default => :test
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ Gem::Specification.new do |gem|
3
+ gem.name = "fluent-plugin-ddl-redis-store"
4
+ gem.email = "hanai@pokelabo.co.jp"
5
+ gem.version = "0.2.1"
6
+ gem.authors = ["moaikids", "HANAI Tohru aka pokehanai"]
7
+ gem.licenses = ["Apache License Version 2.0"]
8
+ gem.summary = %q{Redis(zset/set/list/string/publish) output plugin for Fluentd}
9
+ gem.description = %q{Redis(zset/set/list/string/publish) output plugin for Fluentd...}
10
+ gem.homepage = "https://github.com/pokehanai/fluent-plugin-redis-store"
11
+
12
+ gem.files = `git ls-files`.split($\)
13
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
14
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
15
+ gem.require_paths = ["lib"]
16
+
17
+ gem.add_development_dependency "rake"
18
+ gem.add_development_dependency "test-unit"
19
+ gem.add_runtime_dependency "fluentd", [">= 0.14.15", "< 2"]
20
+ gem.add_runtime_dependency "redis"
21
+ end
@@ -0,0 +1,263 @@
1
+ require 'fluent/plugin/output'
2
+
3
+ module Fluent::Plugin
4
+ class RedisStoreOutput < Output
5
+ Fluent::Plugin.register_output('redis_store', self)
6
+
7
+ helpers :compat_parameters
8
+
9
+ DEFAULT_BUFFER_TYPE = "memory"
10
+
11
+ # redis connection
12
+ config_param :host, :string, :default => '127.0.0.1'
13
+ config_param :port, :integer, :default => 6379
14
+ config_param :path, :string, :default => nil
15
+ config_param :password, :string, :default => nil
16
+ config_param :db, :integer, :default => 0
17
+ config_param :timeout, :float, :default => 5.0
18
+
19
+ # redis command and parameters
20
+ config_param :format_type, :string, :default => 'json'
21
+ config_param :store_type, :string, :default => 'zset'
22
+ config_param :key_prefix, :string, :default => ''
23
+ config_param :key_suffix, :string, :default => ''
24
+ config_param :key, :string, :default => nil
25
+ config_param :key_path, :string, :default => nil
26
+ config_param :score_path, :string, :default => nil
27
+ config_param :value_path, :string, :default => ''
28
+ config_param :key_expire, :integer, :default => -1
29
+ config_param :value_expire, :integer, :default => -1
30
+ config_param :value_length, :integer, :default => -1
31
+ config_param :order, :string, :default => 'asc'
32
+ config_param :collision_policy, :string, :default => nil
33
+ config_set_default :flush_interval, 1
34
+
35
+ config_section :buffer do
36
+ config_set_default :@type, DEFAULT_BUFFER_TYPE
37
+ end
38
+
39
+ def initialize
40
+ super
41
+ require 'redis' unless defined?(Redis) == 'constant'
42
+ require 'msgpack'
43
+ end
44
+
45
+ def configure(conf)
46
+ compat_parameters_convert(conf, :buffer)
47
+ super
48
+
49
+ if @key_path == nil and @key == nil
50
+ raise Fluent::ConfigError, "either key_path or key is required"
51
+ end
52
+ end
53
+
54
+ def start
55
+ super
56
+ if @path
57
+ @redis = Redis.new(:path => @path, :password => @password,
58
+ :timeout => @timeout, :thread_safe => true, :db => @db)
59
+ else
60
+ @redis = Redis.new(:host => @host, :port => @port, :password => @password,
61
+ :timeout => @timeout, :thread_safe => true, :db => @db)
62
+ end
63
+ end
64
+
65
+ def shutdown
66
+ @redis.quit
67
+ super
68
+ end
69
+
70
+ def format(tag, time, record)
71
+ [tag, time.to_f, record].to_msgpack
72
+ end
73
+
74
+ def formatted_to_msgpack_binary?
75
+ true
76
+ end
77
+
78
+ def multi_workers_ready?
79
+ true
80
+ end
81
+
82
+ def write(chunk)
83
+ @redis.pipelined {
84
+ chunk.open { |io|
85
+ begin
86
+ MessagePack::Unpacker.new(io).each { |message|
87
+ begin
88
+ (_, time, record) = message
89
+ case @store_type
90
+ when 'zset'
91
+ operation_for_zset(record, time)
92
+ when 'set'
93
+ operation_for_set(record)
94
+ when 'list'
95
+ operation_for_list(record)
96
+ when 'string'
97
+ operation_for_string(record)
98
+ when 'publish'
99
+ operation_for_publish(record)
100
+ end
101
+ rescue NoMethodError => e
102
+ puts e
103
+ rescue Encoding::UndefinedConversionError => e
104
+ log.error "Plugin error: " + e.to_s
105
+ log.error "Original record: " + record.to_s
106
+ puts e
107
+ end
108
+ }
109
+ rescue EOFError
110
+ # EOFError always occured when reached end of chunk.
111
+ end
112
+ }
113
+ }
114
+ end
115
+
116
+ def operation_for_zset(record, time)
117
+ key = get_key_from(record)
118
+ value = get_value_from(record)
119
+ score = get_score_from(record, time)
120
+ if @collision_policy
121
+ if @collision_policy == 'NX'
122
+ @redis.zadd(key, score, value, :nx => true)
123
+ elsif @collision_policy == 'XX'
124
+ @redis.zadd(key, score, value, :xx => true)
125
+ end
126
+ else
127
+ @redis.zadd(key, score, value)
128
+ end
129
+
130
+ set_key_expire key
131
+ if 0 < @value_expire
132
+ now = Time.now.to_i
133
+ @redis.zremrangebyscore key , '-inf' , (now - @value_expire)
134
+ end
135
+ if 0 < @value_length
136
+ script = generate_zremrangebyrank_script(key, @value_length, @order)
137
+ @redis.eval script
138
+ end
139
+ end
140
+
141
+ def operation_for_set(record)
142
+ key = get_key_from(record)
143
+ value = get_value_from(record)
144
+ @redis.sadd key, value
145
+ set_key_expire key
146
+ end
147
+
148
+ def operation_for_list(record)
149
+ key = get_key_from(record)
150
+ value = get_value_from(record)
151
+
152
+ if @order == 'asc'
153
+ @redis.rpush key, value
154
+ else
155
+ @redis.lpush key, value
156
+ end
157
+ set_key_expire key
158
+ if 0 < @value_length
159
+ script = generate_ltrim_script(key, @value_length, @order)
160
+ @redis.eval script
161
+ end
162
+ end
163
+
164
+ def operation_for_string(record)
165
+ key = get_key_from(record)
166
+ value = get_value_from(record)
167
+ @redis.set key, value
168
+
169
+ set_key_expire key
170
+ end
171
+
172
+ def operation_for_publish(record)
173
+ key = get_key_from(record)
174
+ value = get_value_from(record)
175
+ @redis.publish key, value
176
+ end
177
+
178
+ def generate_zremrangebyrank_script(key, maxlen, order)
179
+ script = "local key = '" + key.to_s + "'\n"
180
+ script += "local maxlen = " + maxlen.to_s + "\n"
181
+ script += "local order ='" + order.to_s + "'\n"
182
+ script += "local len = tonumber(redis.call('ZCOUNT', key, '-inf', '+inf'))\n"
183
+ script += "if len > maxlen then\n"
184
+ script += " if order == 'asc' then\n"
185
+ script += " local l = len - maxlen\n"
186
+ script += " if l >= 0 then\n"
187
+ script += " return redis.call('ZREMRANGEBYRANK', key, 0, l)\n"
188
+ script += " end\n"
189
+ script += " else\n"
190
+ script += " return redis.call('ZREMRANGEBYRANK', key, maxlen, -1)\n"
191
+ script += " end\n"
192
+ script += "end\n"
193
+ return script
194
+ end
195
+
196
+ def generate_ltrim_script(key, maxlen, order)
197
+ script = "local key = '" + key.to_s + "'\n"
198
+ script += "local maxlen = " + maxlen.to_s + "\n"
199
+ script += "local order ='" + order.to_s + "'\n"
200
+ script += "local len = tonumber(redis.call('LLEN', key))\n"
201
+ script += "if len > maxlen then\n"
202
+ script += " if order == 'asc' then\n"
203
+ script += " local l = len - maxlen\n"
204
+ script += " return redis.call('LTRIM', key, l, -1)\n"
205
+ script += " else\n"
206
+ script += " return redis.call('LTRIM', key, 0, maxlen - 1)\n"
207
+ script += " end\n"
208
+ script += "end\n"
209
+ return script
210
+ end
211
+
212
+ def traverse(data, key)
213
+ val = data
214
+ key.split('.').each{ |k|
215
+ if val.has_key?(k)
216
+ val = val[k]
217
+ else
218
+ return nil
219
+ end
220
+ }
221
+ return val
222
+ end
223
+
224
+ def get_key_from(record)
225
+ if @key
226
+ k = @key
227
+ else
228
+ k = traverse(record, @key_path).to_s
229
+ end
230
+ key = @key_prefix + k + @key_suffix
231
+
232
+ raise Fluent::ConfigError, "key is empty" if key == ''
233
+ key
234
+ end
235
+
236
+ def get_value_from(record)
237
+ value = traverse(record, @value_path)
238
+ case @format_type
239
+ when 'json'
240
+ Yajl.dump(value)
241
+ when 'msgpack'
242
+ value.to_msgpack
243
+ else
244
+ value
245
+ end
246
+ end
247
+
248
+ def get_score_from(record, time)
249
+ if @score_path
250
+ traverse(record, @score_path)
251
+ else
252
+ time
253
+ end
254
+ end
255
+
256
+ def set_key_expire(key)
257
+ if 0 < @key_expire
258
+ @redis.expire key, @key_expire
259
+ end
260
+ end
261
+
262
+ end
263
+ end
data/test/helpers.rb ADDED
@@ -0,0 +1,31 @@
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
+ require 'fluent/test/driver/output'
16
+ require 'fluent/test/helpers'
17
+ unless ENV.has_key?('VERBOSE')
18
+ nulllogger = Object.new
19
+ nulllogger.instance_eval {|obj|
20
+ def method_missing(method, *args)
21
+ # pass
22
+ end
23
+ }
24
+ $log = nulllogger
25
+ end
26
+
27
+ require 'fluent/plugin/out_redis_store'
28
+
29
+ class Test::Unit::TestCase
30
+ include Fluent::Test::Helpers
31
+ end
@@ -0,0 +1,418 @@
1
+ require 'helpers'
2
+
3
+ $channel = nil
4
+ $message = nil
5
+
6
+ class Redis
7
+ def initialize(options = {})
8
+ end
9
+
10
+ def pipelined
11
+ yield self
12
+ end
13
+
14
+ def set(key, message)
15
+ $command = :set
16
+ $key = key
17
+ $message = message
18
+ end
19
+
20
+ def rpush(key, message)
21
+ $command = :rpush
22
+ $key = key
23
+ $message = message
24
+ end
25
+
26
+ def lpush(key, message)
27
+ $command = :lpush
28
+ $key = key
29
+ $message = message
30
+ end
31
+
32
+ def sadd(key, message)
33
+ $command = :sadd
34
+ $key = key
35
+ $message = message
36
+ end
37
+
38
+ def zadd(key, score, message)
39
+ $command = :zadd
40
+ $key = key
41
+ $score = score
42
+ $message = message
43
+ end
44
+
45
+ def expire(key, ttl)
46
+ $expire_key = key
47
+ $ttl = ttl
48
+ end
49
+
50
+ def publish(channel, message)
51
+ $command = :publish
52
+ $channel = channel
53
+ $message = message
54
+ end
55
+
56
+ def quit
57
+ end
58
+ end
59
+
60
+ class RedisStoreOutputTest < Test::Unit::TestCase
61
+ def setup
62
+ Fluent::Test.setup
63
+ end
64
+
65
+ def create_driver(conf)
66
+ Fluent::Test::Driver::Output.new(Fluent::Plugin::RedisStoreOutput).configure(conf)
67
+ end
68
+
69
+ def test_configure_defaults
70
+ config = %[
71
+ key_path a
72
+ score_path b
73
+ ]
74
+ d = create_driver(config)
75
+ assert_equal("127.0.0.1", d.instance.host)
76
+ assert_equal(6379, d.instance.port)
77
+ assert_equal(nil, d.instance.path)
78
+ assert_equal(nil, d.instance.password)
79
+ assert_equal(0, d.instance.db)
80
+ assert_equal(5.0, d.instance.timeout)
81
+ assert_equal('json', d.instance.format_type)
82
+ assert_equal('', d.instance.key_prefix)
83
+ assert_equal('', d.instance.key_suffix)
84
+ assert_equal('zset', d.instance.store_type)
85
+ assert_equal('a', d.instance.key_path)
86
+ assert_equal(nil, d.instance.key)
87
+ assert_equal('b', d.instance.score_path)
88
+ assert_equal('', d.instance.value_path)
89
+ assert_equal(-1, d.instance.key_expire)
90
+ assert_equal(-1, d.instance.value_expire)
91
+ assert_equal(-1, d.instance.value_length)
92
+ assert_equal('asc', d.instance.order)
93
+ assert_equal(nil, d.instance.collision_policy)
94
+ end
95
+
96
+ def test_configure_host_port_db
97
+ config = %[
98
+ host 192.168.2.3
99
+ port 9999
100
+ password abc
101
+ db 3
102
+ timeout 7
103
+ key a
104
+ score_path b
105
+ ]
106
+ d = create_driver(config)
107
+ assert_equal "192.168.2.3", d.instance.host
108
+ assert_equal 9999, d.instance.port
109
+ assert_equal nil, d.instance.path
110
+ assert_equal 'abc', d.instance.password
111
+ assert_equal 3, d.instance.db
112
+ assert_equal 7.0, d.instance.timeout
113
+ assert_equal nil, d.instance.key_path
114
+ assert_equal 'a', d.instance.key
115
+ end
116
+
117
+ def test_configure_path
118
+ config = %[
119
+ path /tmp/foo.sock
120
+ key a
121
+ score_path b
122
+ ]
123
+ d = create_driver(config)
124
+ assert_equal "/tmp/foo.sock", d.instance.path
125
+ end
126
+
127
+ def test_configure_exception
128
+ assert_raise(Fluent::ConfigError) do
129
+ create_driver(%[])
130
+ end
131
+ end
132
+
133
+ # def test_write
134
+ # d = create_driver(CONFIG1)
135
+ #
136
+ # time = event_time("2011-01-02 13:14:15 UTC")
137
+ # d.run(default_tag 'test') do
138
+ # d.feed({ "foo" => "bar" }, time)
139
+ # end
140
+ #
141
+ # assert_equal "test", $channel
142
+ # assert_equal(%Q[{"foo":"bar","time":#{time}}], $message)
143
+ # end
144
+
145
+ def get_time
146
+ event_time("2011-01-02 13:14:15 UTC")
147
+ end
148
+
149
+ # it should return whole message
150
+ def test_omit_value_path
151
+ config = %[
152
+ format_type plain
153
+ store_type string
154
+ key_path user
155
+ ]
156
+ d = create_driver(config)
157
+ message = {
158
+ 'user' => 'george',
159
+ 'stat' => { 'attack' => 7 }
160
+ }
161
+ $ttl = nil
162
+ d.run(default_tag: 'test') do
163
+ d.feed(get_time, message)
164
+ end
165
+
166
+ assert_equal "george", $key
167
+ assert_equal message, $message
168
+ assert_equal nil, $ttl
169
+ end
170
+
171
+ def test_key_value_paths
172
+ config = %[
173
+ format_type plain
174
+ store_type string
175
+ key_path user.name
176
+ value_path stat.attack
177
+ key_expire 3
178
+ ]
179
+ d = create_driver(config)
180
+ message = {
181
+ 'user' => { 'name' => 'george' },
182
+ 'stat' => { 'attack' => 7 }
183
+ }
184
+ $ttl = nil
185
+ d.run(default_tag: 'test') do
186
+ d.feed(get_time, message)
187
+ end
188
+
189
+ assert_equal "george", $key
190
+ assert_equal 7, $message
191
+ assert_equal 3, $ttl
192
+ end
193
+
194
+ def test_json
195
+ config = %[
196
+ format_type json
197
+ store_type string
198
+ key_path user
199
+ ]
200
+ d = create_driver(config)
201
+ message = {
202
+ 'user' => 'george',
203
+ 'stat' => { 'attack' => 7 }
204
+ }
205
+ $ttl = nil
206
+ d.run(default_tag: 'test') do
207
+ d.feed(get_time, message)
208
+ end
209
+
210
+ assert_equal "george", $key
211
+ assert_equal message.to_json, $message
212
+ end
213
+
214
+ def test_json_with_malformed_utf8
215
+ config = %[
216
+ format_type json
217
+ store_type string
218
+ key_path user
219
+ ]
220
+ d = create_driver(config)
221
+ message = {
222
+ 'user' => 'george',
223
+ 'data' => ([0x80, 0x80].map {|x| x.chr}.join).force_encoding('UTF-8')
224
+
225
+ }
226
+ $ttl = nil
227
+ d.run(default_tag: 'test') do
228
+ d.feed(get_time, message)
229
+ end
230
+
231
+ assert_equal message, Yajl.load($message, :check_utf8 => false)
232
+ end
233
+
234
+ def test_msgpack
235
+ config = %[
236
+ format_type msgpack
237
+ store_type string
238
+ key_path user
239
+ ]
240
+ d = create_driver(config)
241
+ message = {
242
+ 'user' => 'george',
243
+ 'stat' => { 'attack' => 7 }
244
+ }
245
+ $ttl = nil
246
+ d.run(default_tag: 'test') do
247
+ d.feed(get_time, message)
248
+ end
249
+
250
+ assert_equal "george", $key
251
+ assert_equal message.to_msgpack, $message
252
+ end
253
+
254
+ def test_list_asc
255
+ config = %[
256
+ format_type plain
257
+ store_type list
258
+ key_path user
259
+ ]
260
+ d = create_driver(config)
261
+ message = {
262
+ 'user' => 'george',
263
+ 'stat' => { 'attack' => 7 }
264
+ }
265
+ d.run(default_tag: 'test') do
266
+ d.feed(get_time, message)
267
+ end
268
+
269
+ assert_equal :rpush, $command
270
+ assert_equal "george", $key
271
+ assert_equal message, $message
272
+ end
273
+
274
+ def test_list_desc
275
+ config = %[
276
+ format_type plain
277
+ store_type list
278
+ key_path user
279
+ order desc
280
+ ]
281
+ d = create_driver(config)
282
+ message = {
283
+ 'user' => 'george',
284
+ 'stat' => { 'attack' => 7 }
285
+ }
286
+ d.run(default_tag: 'test') do
287
+ d.feed(get_time, message)
288
+ end
289
+
290
+ assert_equal :lpush, $command
291
+ assert_equal "george", $key
292
+ assert_equal message, $message
293
+ end
294
+
295
+ def test_set
296
+ config = %[
297
+ format_type plain
298
+ store_type set
299
+ key_path user
300
+ order desc
301
+ ]
302
+ d = create_driver(config)
303
+ message = {
304
+ 'user' => 'george',
305
+ 'stat' => { 'attack' => 7 }
306
+ }
307
+ d.run(default_tag: 'test') do
308
+ d.feed(get_time, message)
309
+ end
310
+
311
+ assert_equal :sadd, $command
312
+ assert_equal "george", $key
313
+ assert_equal message, $message
314
+ end
315
+
316
+ def test_zset
317
+ config = %[
318
+ format_type plain
319
+ store_type zset
320
+ key_path user
321
+ score_path result
322
+ ]
323
+
324
+ d = create_driver(config)
325
+ message = {
326
+ 'user' => 'george',
327
+ 'stat' => { 'attack' => 7 },
328
+ 'result' => 81
329
+ }
330
+ d.run(default_tag: 'test') do
331
+ d.feed(get_time, message)
332
+ end
333
+
334
+ assert_equal :zadd, $command
335
+ assert_equal "george", $key
336
+ assert_equal 81, $score
337
+ assert_equal message, $message
338
+ end
339
+
340
+ def test_zset_with_no_score_path
341
+ config = %[
342
+ format_type plain
343
+ store_type zset
344
+ key_path user
345
+ ]
346
+
347
+ d = create_driver(config)
348
+ message = {
349
+ 'user' => 'george',
350
+ 'stat' => { 'attack' => 7 },
351
+ 'result' => 81
352
+ }
353
+ d.run(default_tag: 'test') do
354
+ d.feed(get_time, message)
355
+ end
356
+
357
+ assert_equal :zadd, $command
358
+ assert_equal "george", $key
359
+ assert_equal get_time, $score
360
+ assert_equal message, $message
361
+ end
362
+
363
+ def test_publish
364
+ config = %[
365
+ format_type plain
366
+ store_type publish
367
+ key_path user
368
+ ]
369
+
370
+ d = create_driver(config)
371
+ message = {
372
+ 'user' => 'george'
373
+ }
374
+ d.run(default_tag: 'test') do
375
+ d.feed(get_time, message)
376
+ end
377
+
378
+ assert_equal :publish, $command
379
+ assert_equal "george", $channel
380
+ assert_equal message, $message
381
+ end
382
+
383
+ def test_empty_key
384
+ config = %[
385
+ format_type plain
386
+ store_type string
387
+ key_path none
388
+ ]
389
+
390
+ d = create_driver(config)
391
+ message = { 'user' => 'george' }
392
+ suppress_output do
393
+ assert_raise(Fluent::ConfigError) do
394
+ d.run(default_tag: 'test') do
395
+ d.feed(get_time, message)
396
+ end
397
+ end
398
+ end
399
+ end
400
+
401
+ def suppress_output
402
+ begin
403
+ original_stderr = $stderr.clone
404
+ original_stdout = $stdout.clone
405
+ $stderr.reopen(File.new('/dev/null', 'w'))
406
+ $stdout.reopen(File.new('/dev/null', 'w'))
407
+ retval = yield
408
+ rescue Exception => e
409
+ $stdout.reopen(original_stdout)
410
+ $stderr.reopen(original_stderr)
411
+ raise e
412
+ ensure
413
+ $stdout.reopen(original_stdout)
414
+ $stderr.reopen(original_stderr)
415
+ end
416
+ retval
417
+ end
418
+ end
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fluent-plugin-ddl-redis-store
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ platform: ruby
6
+ authors:
7
+ - moaikids
8
+ - HANAI Tohru aka pokehanai
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2024-11-20 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: test-unit
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: fluentd
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: 0.14.15
49
+ - - "<"
50
+ - !ruby/object:Gem::Version
51
+ version: '2'
52
+ type: :runtime
53
+ prerelease: false
54
+ version_requirements: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: 0.14.15
59
+ - - "<"
60
+ - !ruby/object:Gem::Version
61
+ version: '2'
62
+ - !ruby/object:Gem::Dependency
63
+ name: redis
64
+ requirement: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ type: :runtime
70
+ prerelease: false
71
+ version_requirements: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ description: Redis(zset/set/list/string/publish) output plugin for Fluentd...
77
+ email: hanai@pokelabo.co.jp
78
+ executables: []
79
+ extensions: []
80
+ extra_rdoc_files: []
81
+ files:
82
+ - ".gitignore"
83
+ - AUTHORS
84
+ - Gemfile
85
+ - README.md
86
+ - Rakefile
87
+ - fluent-plugin-ddl-redis-store.gemspec
88
+ - lib/fluent/plugin/out_redis_store.rb
89
+ - test/helpers.rb
90
+ - test/plugin/test_out_redis_publish.rb
91
+ homepage: https://github.com/pokehanai/fluent-plugin-redis-store
92
+ licenses:
93
+ - Apache License Version 2.0
94
+ metadata: {}
95
+ post_install_message:
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubygems_version: 3.0.3.1
111
+ signing_key:
112
+ specification_version: 4
113
+ summary: Redis(zset/set/list/string/publish) output plugin for Fluentd
114
+ test_files:
115
+ - test/helpers.rb
116
+ - test/plugin/test_out_redis_publish.rb