logstash-output-redisearch 0.1.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: 60a5ad2c1a084053f9275022336e2e32620c08ff5a44041743e7a3b4860bf063
4
+ data.tar.gz: 8769d6da6888847da02eab1ef5972d0015a0567ec4246b73d1380fb58039fbac
5
+ SHA512:
6
+ metadata.gz: df4d0505f0e869b38fcb74de6749ff86e81a483a57d9eab620515bd1494277ede9b6115ca1a3adaafaaa07f46d8aae4191c0253e2a732974b29446da98b2ad21
7
+ data.tar.gz: d43c3d54cf4363fb1a4a25a102a4a21f55b1b6d83ec94b80d4f567bdfc676269b3ef351f717212f942e0749e0aee6f050c11a84bb1ac8c87bfe930c5ee944e46
data/CHANGELOG.md ADDED
@@ -0,0 +1,2 @@
1
+ ## 0.1.0
2
+ - Plugin created with the logstash plugin generator
data/CONTRIBUTORS ADDED
@@ -0,0 +1,10 @@
1
+ The following is a list of people who have contributed ideas, code, bug
2
+ reports, or in general have helped logstash along its way.
3
+
4
+ Contributors:
5
+ * - HashedIn
6
+
7
+ Note: If you've sent us patches, bug reports, or otherwise contributed to
8
+ Logstash, and you aren't on the list above and want to be, please let us know
9
+ and we'll make sure you're here. Contributions from folks like you are what make
10
+ open source awesome.
data/DEVELOPER.md ADDED
@@ -0,0 +1,2 @@
1
+ # logstash-output-redisearch
2
+ Example output plugin. This should help bootstrap your effort to write your own output plugin!
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,11 @@
1
+ Licensed under the Apache License, Version 2.0 (the "License");
2
+ you may not use this file except in compliance with the License.
3
+ You may obtain a copy of the License at
4
+
5
+ http://www.apache.org/licenses/LICENSE-2.0
6
+
7
+ Unless required by applicable law or agreed to in writing, software
8
+ distributed under the License is distributed on an "AS IS" BASIS,
9
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ See the License for the specific language governing permissions and
11
+ limitations under the License.
data/README.md ADDED
@@ -0,0 +1,181 @@
1
+ # Logstash Output Plugin
2
+
3
+ This logstash output plugin is for Redisearch.
4
+ - Note: Plugin has not been tested with cluster mode.
5
+
6
+ ### 1. Plugin Development and Testing
7
+
8
+ #### Requirements
9
+ * JRuby (Use Ruby Version Manger(RVM))
10
+ * JDK
11
+ * Git
12
+ * bundler
13
+ * Redisearch
14
+ * Logstash
15
+
16
+ #### Install requirements
17
+ * RVM
18
+ ```bash
19
+ gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
20
+ \curl -sSL https://get.rvm.io | bash
21
+ source ~/.rvm/scripts/rvm
22
+ ```
23
+ * JRuby
24
+ ```bash
25
+ rvm install jruby
26
+ ```
27
+
28
+ * JDK
29
+ ```bash
30
+ sudo apt install default-jdk
31
+ echo "export JAVA_HOME=$(dirname $(dirname $(readlink -f $(which javac) )))" >> ~/.profile
32
+ source ~/.profile
33
+ ```
34
+
35
+ * bundler
36
+ ```bash
37
+ gem install bundler
38
+ ```
39
+
40
+ * Redisearch
41
+ ```bash
42
+ git clone --recursive https://github.com/RediSearch/RediSearch.git
43
+ make build
44
+ make run
45
+ ```
46
+
47
+ #### Code
48
+
49
+ - Clone Project
50
+ ```bash
51
+ git clone https://github.com/hashedin/logstash-output-redisearch.git
52
+ ```
53
+ - Use JRuby
54
+ ```bash
55
+ rvm use jruby
56
+ ```
57
+
58
+ - Install dependencies
59
+ ```bash
60
+ bundle install
61
+ ```
62
+
63
+ #### Test
64
+
65
+ - Run tests
66
+
67
+ ```bash
68
+ bundle exec rspec
69
+ ```
70
+
71
+ ### 2. Running your Plugin in Logstash
72
+
73
+ * Install Logstash
74
+ ```bash
75
+ wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -
76
+ sudo apt-get install apt-transport-https
77
+ echo "deb https://artifacts.elastic.co/packages/7.x/apt stable main" | sudo tee -a /etc/apt/sources.list.d/elastic-7.x.list
78
+ sudo apt-get update && sudo apt-get install logstash
79
+ sudo /usr/share/logstash/bin/system-install /etc/logstash/startup.options systemd
80
+ ```
81
+
82
+ #### Run in a local Logstash
83
+
84
+ - Build Gemfile
85
+
86
+ ```bash
87
+ gem build logstash-output-redisearch.gemspec
88
+ ```
89
+
90
+ - Deploy Gemfile to Logstash
91
+
92
+ ```bash
93
+ bin/logstash-plugin install /path/to/logstash-output-redisearch-0.1.0.gem
94
+ ```
95
+
96
+ - Verify installed plugin
97
+ ```bash
98
+ bin/logstash-plugin list
99
+ There should be logstash-output-redisearch
100
+ ```
101
+ #### Start logstash output plugin
102
+
103
+ - Configuration options
104
+
105
+ | Name | Description | Type | Default |
106
+ | --- | --- | --- | --- |
107
+ | host | Redis-server IP address | string | "127.0.0.1" |
108
+ | port | Redis-server port number | number | 6379 |
109
+ | index | Name an index in redisearch | string | "logstash-current-date" |
110
+ | batch_events | Max number of events in a buffer before flush | number | 50 |
111
+ | batch_timeout | Max interval to pass before flush | number | 5 |
112
+ | ssl | SSL authentication | boolean | false |
113
+ | password | Password for authentication | password | - |
114
+
115
+ * Usage
116
+ ```bash
117
+ output {
118
+ redisearch {
119
+ }
120
+ }
121
+ ```
122
+ OR
123
+
124
+ ```bash
125
+ output {
126
+ redisearch {
127
+ host => '192.168.0.1'
128
+ port => 6379
129
+ index => logstash
130
+ batch_events => 20
131
+ batch_timeout => 2
132
+ ssl => true
133
+ password => "123"
134
+ }
135
+ }
136
+ ```
137
+
138
+ #### Example
139
+
140
+ Let's create a logstash pipleline using filebeat as input plugin and redisearch as output plugin:
141
+ 1. Install filebeat and configure /etc/filebeat/filebeat.yml as following:
142
+ - Install filebeat:
143
+ ```bash
144
+ sudo apt-get install filebeat
145
+ ```
146
+ - Enable filebeat input to read from file:
147
+ ```bash
148
+ filebeat.inputs:
149
+ enabled: true
150
+ paths:
151
+ - /path/to/logfile
152
+ ```
153
+
154
+ - Change filebeat output from elasticsearch to logstash:
155
+ ```bash
156
+ output.logstash:
157
+ hosts: [“localhost:5044”]
158
+ ```
159
+
160
+ 2. Create a conf file in /etc/logstash/conf.d
161
+ ```bash
162
+ input {
163
+ beats {
164
+ Port => 5044
165
+ }
166
+ output {
167
+ redisearch {
168
+ }
169
+ }
170
+ ```
171
+
172
+ 3. After configuring, restart logstash and filebeat services and check the data stashing into redisearch.
173
+ ```bash
174
+ sudo service logstash restart
175
+ sudo service filebeat restart
176
+ ```
177
+
178
+ #### References
179
+
180
+ * https://github.com/logstash-plugins/logstash-output-redis : Redis Output Plugin
181
+ * https://github.com/logstash-plugins/logstash-output-elasticsearch : Elasticsearch Output Plugin
@@ -0,0 +1,118 @@
1
+ # encoding: utf-8
2
+ # require "logstash/outputs/template"
3
+ require "logstash/outputs/template"
4
+ require "logstash/outputs/base"
5
+ require 'redisearch-rb'
6
+ require "stud/buffer"
7
+ require 'json'
8
+ require 'time'
9
+
10
+ # An redisearch output will store data into Redisearch.
11
+ class LogStash::Outputs::Redisearch < LogStash::Outputs::Base
12
+ include Stud::Buffer
13
+ config_name "redisearch"
14
+ default :codec, "json"
15
+
16
+ # Hostname of rediserver to connect to rediserver.
17
+ config :host, :validate => :string, :default => "127.0.0.1"
18
+
19
+ # Port number to connect to rediserver
20
+ config :port, :validate => :number, :default => 6379
21
+
22
+ # Index name to create or to connect the existing old spec
23
+ config :index, :validate => :string, :default => nil
24
+
25
+ # Interval to reconnect if Failure in redis connection
26
+ config :reconnect_interval, :validate => :number, :default => 1
27
+
28
+ # Max number of events to add in a list
29
+ config :batch_events, :validate => :number, :default => 50
30
+
31
+ # Max interval to pass before flush
32
+ config :batch_timeout, :validate => :number, :default => 5
33
+
34
+ # SSL flag for aunthentication
35
+ config :ssl, :validate => :boolean, :default => false
36
+
37
+ # Password for aunthentication
38
+ config :password, :validate => :password
39
+
40
+ # Method is a constructor to this class.
41
+ # Used to intialize buffer, redisearch client and also to create a index if it is not present
42
+ public
43
+ def register
44
+
45
+ buffer_initialize(
46
+ :max_items => @batch_events,
47
+ :max_interval => @batch_timeout,
48
+ )
49
+
50
+ params = {
51
+ "host"=>@host,
52
+ "port"=>@port,
53
+ "index"=>@index,
54
+ "ssl"=>@ssl
55
+ }
56
+
57
+ if @password
58
+ params["password"] = @password.value
59
+ end
60
+
61
+ @idx = Index.new(params)
62
+ @redisearch_client = @idx.connect()
63
+ @codec.on_event(&method(:send_to_redisearch))
64
+
65
+ end # def register
66
+
67
+ # Method is to receive event and encode it in json format.
68
+ public
69
+ def receive(event)
70
+ begin
71
+ @codec.encode(event)
72
+ rescue StandardError => e
73
+ @logger.warn("Error encoding event", :exception => e,
74
+ :event => event)
75
+ sleep @reconnect_interval
76
+ retry
77
+ end
78
+ end # def event
79
+
80
+ # Method is called from Stud::Buffer when max_items/max_interval is reached
81
+ def flush(events, close=false)
82
+ #buffer_flush should pass here the :final boolean value.
83
+ @redisearch_client.add_docs(events)
84
+ @logger.info("Buffer Inserted Successfully", :length => events.length)
85
+ end
86
+
87
+ # Method is called from Stud::Buffer when an error occurs
88
+ def on_flush_error(e)
89
+ @logger.warn("Failed to send backlog of events to Redisearch",
90
+ :exception => e,
91
+ :backtrace => e.backtrace
92
+ )
93
+ end
94
+
95
+ # Method is for final bookkeeping and cleanup when plugin thread exit
96
+ def close
97
+ # Force full flush call to ensure that all accumulated messages are flushed.
98
+ buffer_flush(:final => true)
99
+ end
100
+
101
+ # Method to assign uuid to each event (formatting event as per document required by redisearch)
102
+ # and to append each event to buffer
103
+ def send_to_redisearch(event, payload)
104
+ begin
105
+ doc_data = JSON.parse(payload)
106
+ doc_id = @idx.get_id()
107
+ document = [doc_id,doc_data]
108
+ buffer_receive(document)
109
+
110
+ rescue => e
111
+ @logger.warn("Failed to send event to Redisearch", :event => event,
112
+ :exception => e,
113
+ :backtrace => e.backtrace)
114
+ sleep @reconnect_interval
115
+ retry
116
+ end
117
+ end
118
+ end # class LogStash::Outputs::Redisearch
@@ -0,0 +1,9 @@
1
+ {
2
+ "index": "logstash-*",
3
+ "schema": {
4
+ "@timestamp": ["TEXT", "SORTABLE"],
5
+ "host" : "TEXT",
6
+ "@version": "TEXT",
7
+ "message": "TEXT"
8
+ }
9
+ }
@@ -0,0 +1,65 @@
1
+ require 'json'
2
+ require 'redisearch-rb'
3
+ require 'time'
4
+ require 'redis'
5
+ require 'securerandom'
6
+ require 'base64'
7
+
8
+ # Redisearch index management
9
+ class Index
10
+ # initialize and create redis instance using params value
11
+ def initialize(params)
12
+ begin
13
+
14
+ @template_data = read_template()
15
+ @redis = Redis.new(
16
+ host: params["host"],
17
+ port: params["port"],
18
+ ssl: params["ssl"],
19
+ password:params["password"])
20
+
21
+ if params["index"] == nil
22
+ time = Time.new
23
+ @idx_name = @template_data['index'].sub! '*', time.strftime("%Y-%m-%d")
24
+ else
25
+ @idx_name = params["index"]
26
+ end
27
+
28
+ rescue => e
29
+ @logger.debug("Exception in Index initialization",e)
30
+ end
31
+ end
32
+
33
+
34
+ # Reads json file and returns data
35
+ def read_template()
36
+ begin
37
+ filepath = ::File.expand_path('template.json', ::File.dirname(__FILE__))
38
+ file_data = ::IO.read(filepath)
39
+ data = JSON.load(file_data)
40
+ rescue => e
41
+ @logger.debug("Exception in reading template", e)
42
+ end
43
+ return data
44
+ end
45
+
46
+ # Creates redisearch instance using redis instance.
47
+ # Using redisearch instance, connects to index if it is present, else creates a new index
48
+ def connect()
49
+ begin
50
+ @rs = RediSearch.new(@idx_name,@redis)
51
+ @rs.info()
52
+ rescue
53
+ @schema = @template_data['schema']
54
+ @rs.create_index(@schema)
55
+ end
56
+ return @rs
57
+ end
58
+
59
+ # Id for each document in redisearch
60
+ def get_id()
61
+ uuid = SecureRandom.uuid
62
+ id = Base64.encode64([ uuid.tr('-', '') ].pack('H*')).gsub(/\=*\n/, '')
63
+ return id
64
+ end
65
+ end #Class Index
@@ -0,0 +1,29 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'logstash-output-redisearch'
3
+ s.version = '0.1.1'
4
+ s.licenses = ['Apache-2.0']
5
+ s.summary = 'Stash logs in Redisearch'
6
+ s.description = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program"
7
+ s.homepage = 'https://github.com/hashedin/logstash-output-redisearch'
8
+ s.authors = ['HashedIn Technologies']
9
+ s.email = 'redis-connectors@hashedin.com'
10
+ s.require_paths = ['lib']
11
+
12
+ # Files
13
+ s.files = Dir['lib/**/*','spec/**/*','vendor/**/*','*.gemspec','*.md','CONTRIBUTORS','Gemfile','LICENSE','NOTICE.TXT']
14
+ # Tests
15
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
16
+
17
+ # Special flag to let us know this is actually a logstash plugin
18
+ s.metadata = { "logstash_plugin" => "true", "logstash_group" => "output" }
19
+
20
+ # Gem dependencies
21
+ s.add_runtime_dependency "logstash-core-plugin-api", "~> 2.0"
22
+ s.add_runtime_dependency "logstash-codec-plain", '~> 3.0'
23
+ s.add_runtime_dependency 'logstash-codec-json', '~> 3.0'
24
+ s.add_runtime_dependency 'redis', '~> 4'
25
+ s.add_runtime_dependency 'redisearch-rb', '~> 1'
26
+ s.add_development_dependency 'logstash-devutils'
27
+ s.add_development_dependency 'flores'
28
+
29
+ end
@@ -0,0 +1,28 @@
1
+ require "logstash/devutils/rspec/spec_helper"
2
+ require "logstash/outputs/redisearch"
3
+ require "redisearch"
4
+
5
+ describe LogStash::Outputs::Redisearch do
6
+
7
+ context "batch of events" do
8
+
9
+ let(:config) {
10
+ {
11
+ "batch_events" => 10,
12
+ "batch_timeout" => 1,
13
+ }
14
+ }
15
+ let(:redisearch) { described_class.new(config) }
16
+
17
+ it "should call buffer_receive" do
18
+ redisearch.register
19
+ expect(redisearch).to receive(:buffer_receive).exactly(100).times.and_call_original
20
+ expect(redisearch).to receive(:flush).exactly(10).times
21
+ expect(redisearch).not_to receive(:on_flush_error)
22
+
23
+ 100.times do |i|
24
+ expect{redisearch.receive(LogStash::Event.new({"message" => "test-#{i}"}))}.to_not raise_error
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,47 @@
1
+ # encoding: utf-8
2
+ require "logstash/devutils/rspec/spec_helper"
3
+ require "logstash/outputs/redisearch"
4
+ require "logstash/event"
5
+ require "redisearch-rb"
6
+ require "flores/random"
7
+ require "redis"
8
+
9
+ describe LogStash::Outputs::Redisearch do
10
+ context "to check if events are inserted to redisearch" do
11
+ subject { described_class.new(config) }
12
+
13
+ options = {
14
+ "host" => "127.0.0.1",
15
+ "port" => 6379,
16
+ "index" => "test_idx",
17
+ }
18
+
19
+ let(:config) {
20
+ options
21
+ }
22
+ let(:event_count) { Flores::Random.integer(0..1000) }
23
+ let(:message) { Flores::Random.text(0..2) }
24
+ redis = Redis.new(host: options["host"], port: options["port"])
25
+ rs = RediSearch.new(options["index"],redis)
26
+
27
+ before do
28
+ subject.register
29
+ event_count.times do |i|
30
+ event = LogStash::Event.new("sequence" => i, "message" => message)
31
+ subject.receive(event)
32
+ end
33
+ subject.close
34
+ end
35
+
36
+ it "search for a text in redisearch" do
37
+ info = rs.search("message")
38
+ insist { info.count } == 0
39
+ end
40
+
41
+ it "count number of docs in redisearch are same as number of events" do
42
+ info = rs.info()
43
+ insist { info['num_docs'].to_i } == event_count
44
+ end
45
+
46
+ end
47
+ end
metadata ADDED
@@ -0,0 +1,159 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: logstash-output-redisearch
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - HashedIn Technologies
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-05-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '2.0'
19
+ name: logstash-core-plugin-api
20
+ prerelease: false
21
+ type: :runtime
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '3.0'
33
+ name: logstash-codec-plain
34
+ prerelease: false
35
+ type: :runtime
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.0'
41
+ - !ruby/object:Gem::Dependency
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '3.0'
47
+ name: logstash-codec-json
48
+ prerelease: false
49
+ type: :runtime
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '4'
61
+ name: redis
62
+ prerelease: false
63
+ type: :runtime
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '4'
69
+ - !ruby/object:Gem::Dependency
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '1'
75
+ name: redisearch-rb
76
+ prerelease: false
77
+ type: :runtime
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1'
83
+ - !ruby/object:Gem::Dependency
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ name: logstash-devutils
90
+ prerelease: false
91
+ type: :development
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ name: flores
104
+ prerelease: false
105
+ type: :development
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: This gem is a Logstash plugin required to be installed on top of the
112
+ Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This
113
+ gem is not a stand-alone program
114
+ email: redis-connectors@hashedin.com
115
+ executables: []
116
+ extensions: []
117
+ extra_rdoc_files: []
118
+ files:
119
+ - CHANGELOG.md
120
+ - CONTRIBUTORS
121
+ - DEVELOPER.md
122
+ - Gemfile
123
+ - LICENSE
124
+ - README.md
125
+ - lib/logstash/outputs/redisearch.rb
126
+ - lib/logstash/outputs/template.json
127
+ - lib/logstash/outputs/template.rb
128
+ - logstash-output-redisearch.gemspec
129
+ - spec/outputs/batch_spec.rb
130
+ - spec/outputs/redisearch_spec.rb
131
+ homepage: https://github.com/hashedin/logstash-output-redisearch
132
+ licenses:
133
+ - Apache-2.0
134
+ metadata:
135
+ logstash_plugin: 'true'
136
+ logstash_group: output
137
+ post_install_message:
138
+ rdoc_options: []
139
+ require_paths:
140
+ - lib
141
+ required_ruby_version: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ required_rubygems_version: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - ">="
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
151
+ requirements: []
152
+ rubyforge_project:
153
+ rubygems_version: 2.7.10
154
+ signing_key:
155
+ specification_version: 4
156
+ summary: Stash logs in Redisearch
157
+ test_files:
158
+ - spec/outputs/batch_spec.rb
159
+ - spec/outputs/redisearch_spec.rb