logstash-output-rados 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +2 -0
- data/CONTRIBUTORS +11 -0
- data/DEVELOPER.md +16 -0
- data/Gemfile +2 -0
- data/LICENSE +13 -0
- data/NOTICE.TXT +5 -0
- data/README.md +88 -0
- data/lib/logstash/outputs/rados.rb +374 -0
- data/logstash-output-rados.gemspec +29 -0
- data/spec/integration/rados_spec.rb +87 -0
- data/spec/outputs/rados_spec.rb +279 -0
- data/spec/supports/helpers.rb +11 -0
- metadata +150 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 973fd1939d1d97304d5bc37141b68ecbd78e9e83
|
4
|
+
data.tar.gz: 8e0d1a2cdf98b8233e008d661eef46925ed8fd4f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 499d69316c3b4547281665a0c59d1f6d96ee472dce147ce42811e8aff979f8e14893a68697dbb1afdd0e450c85cb6f65bf366028e3f9f125dc95a244227619fa
|
7
|
+
data.tar.gz: 77fb5bfab3c8cfd1feb04ecf34bd251431f9fd3060ce1f900d7a8ea21ed326f626c887aafb6e816663a84f40da2c6f8e10869b439d286519c50f578f34dbe603
|
data/CHANGELOG.md
ADDED
data/CONTRIBUTORS
ADDED
@@ -0,0 +1,11 @@
|
|
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
|
+
* Balint Csergo (deathowl)
|
6
|
+
* Thanks for the developers of the S3 plugin.
|
7
|
+
|
8
|
+
Note: If you've sent us patches, bug reports, or otherwise contributed to
|
9
|
+
Logstash, and you aren't on the list above and want to be, please let us know
|
10
|
+
and we'll make sure you're here. Contributions from folks like you are what make
|
11
|
+
open source awesome.
|
data/DEVELOPER.md
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
[Missing the other part of the readme]
|
2
|
+
|
3
|
+
## Running the tests
|
4
|
+
|
5
|
+
```
|
6
|
+
bundle install
|
7
|
+
bundle rspec
|
8
|
+
```
|
9
|
+
|
10
|
+
If you want to run the integration test you have to have access to a ceph cluster, and a real bucket
|
11
|
+
|
12
|
+
```
|
13
|
+
RADOS_LOGSTASH_TEST_POOL=mytest bundle exec rspec spec/integration/rados_spec.rb --tag integration
|
14
|
+
RADOS_LOGSTASH_TEST_POOL=mytest bundle exec rspec spec/outputs/rados_spec.rb
|
15
|
+
|
16
|
+
```
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright (c) 2012–2015 Elasticsearch <http://www.elastic.co>
|
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/NOTICE.TXT
ADDED
data/README.md
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
# Logstash Plugin
|
2
|
+
|
3
|
+
|
4
|
+
|
5
|
+
This is a plugin for [Logstash](https://github.com/elastic/logstash).
|
6
|
+
|
7
|
+
It is fully free and fully open source. The license is Apache 2.0, meaning you are pretty much free to use it however you want in whatever way.
|
8
|
+
|
9
|
+
## Documentation
|
10
|
+
|
11
|
+
Logstash provides infrastructure to automatically generate documentation for this plugin. We use the asciidoc format to write documentation so any comments in the source code will be first converted into asciidoc and then into html. All plugin documentation are placed under one [central location](http://www.elastic.co/guide/en/logstash/current/).
|
12
|
+
|
13
|
+
- For formatting code or config example, you can use the asciidoc `[source,ruby]` directive
|
14
|
+
- For more asciidoc formatting tips, see the excellent reference here https://github.com/elastic/docs#asciidoc-guide
|
15
|
+
|
16
|
+
## Need Help?
|
17
|
+
|
18
|
+
Need help? Try #logstash on freenode IRC or the https://discuss.elastic.co/c/logstash discussion forum.
|
19
|
+
|
20
|
+
## Developing
|
21
|
+
|
22
|
+
### 1. Plugin Developement and Testing
|
23
|
+
|
24
|
+
#### Code
|
25
|
+
- To get started, you'll need JRuby with the Bundler gem installed.
|
26
|
+
|
27
|
+
- Create a new plugin or clone and existing from the GitHub [logstash-plugins](https://github.com/logstash-plugins) organization. We also provide [example plugins](https://github.com/logstash-plugins?query=example).
|
28
|
+
|
29
|
+
- Install dependencies
|
30
|
+
```sh
|
31
|
+
bundle install
|
32
|
+
```
|
33
|
+
|
34
|
+
#### Test
|
35
|
+
|
36
|
+
- Update your dependencies
|
37
|
+
|
38
|
+
```sh
|
39
|
+
bundle install
|
40
|
+
```
|
41
|
+
|
42
|
+
- Run tests
|
43
|
+
|
44
|
+
```sh
|
45
|
+
bundle exec rspec
|
46
|
+
```
|
47
|
+
|
48
|
+
### 2. Running your unpublished Plugin in Logstash
|
49
|
+
|
50
|
+
#### 2.1 Run in a local Logstash clone
|
51
|
+
|
52
|
+
- Edit Logstash `Gemfile` and add the local plugin path, for example:
|
53
|
+
```ruby
|
54
|
+
gem "logstash-filter-awesome", :path => "/your/local/logstash-filter-awesome"
|
55
|
+
```
|
56
|
+
- Install plugin
|
57
|
+
```sh
|
58
|
+
bin/plugin install --no-verify
|
59
|
+
```
|
60
|
+
- Run Logstash with your plugin
|
61
|
+
```sh
|
62
|
+
bin/logstash -e 'filter {awesome {}}'
|
63
|
+
```
|
64
|
+
At this point any modifications to the plugin code will be applied to this local Logstash setup. After modifying the plugin, simply rerun Logstash.
|
65
|
+
|
66
|
+
#### 2.2 Run in an installed Logstash
|
67
|
+
|
68
|
+
You can use the same **2.1** method to run your plugin in an installed Logstash by editing its `Gemfile` and pointing the `:path` to your local plugin development directory or you can build the gem and install it using:
|
69
|
+
|
70
|
+
- Build your plugin gem
|
71
|
+
```sh
|
72
|
+
gem build logstash-filter-awesome.gemspec
|
73
|
+
```
|
74
|
+
- Install the plugin from the Logstash home
|
75
|
+
```sh
|
76
|
+
bin/plugin install /your/local/plugin/logstash-filter-awesome.gem
|
77
|
+
```
|
78
|
+
- Start Logstash and proceed to test the plugin
|
79
|
+
|
80
|
+
## Contributing
|
81
|
+
|
82
|
+
All contributions are welcome: ideas, patches, documentation, bug reports, complaints, and even something you drew up on a napkin.
|
83
|
+
|
84
|
+
Programming is not a required skill. Whatever you've seen about open source and maintainers or community members saying "send patches or die" - you will not see that here.
|
85
|
+
|
86
|
+
It is more important to the community that you are able to contribute.
|
87
|
+
|
88
|
+
For more information about contributing, see the [CONTRIBUTING](https://github.com/elastic/logstash/blob/master/CONTRIBUTING.md) file.
|
@@ -0,0 +1,374 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "logstash/outputs/base"
|
3
|
+
require "logstash/namespace"
|
4
|
+
require "stud/temporary"
|
5
|
+
require "stud/task"
|
6
|
+
require "socket" # for Socket.gethostname
|
7
|
+
require "thread"
|
8
|
+
require "tmpdir"
|
9
|
+
require "fileutils"
|
10
|
+
|
11
|
+
|
12
|
+
# INFORMATION:
|
13
|
+
#
|
14
|
+
# This plugin sends logstash events to Ceph.
|
15
|
+
# To use it you need to have a properly configured librados and a valid ceph cluster.
|
16
|
+
# Make sure you have permissions to write files on Ceph. Also be sure to run logstash as super user to establish a connection.
|
17
|
+
#
|
18
|
+
#
|
19
|
+
# This plugin outputs temporary files to "/opt/logstash/rados_temp/". If you want, you can change the path at the start of register method.
|
20
|
+
# These files have a special name, for example:
|
21
|
+
#
|
22
|
+
# ls.rados.ip-10-228-27-95.2013-04-18T10.00.tag_hello.part0.txt
|
23
|
+
#
|
24
|
+
# ls.rados : indicate logstash plugin rados
|
25
|
+
#
|
26
|
+
# "ip-10-228-27-95" : indicates the ip of your machine.
|
27
|
+
# "2013-04-18T10.00" : represents the time whenever you specify time_file.
|
28
|
+
# "tag_hello" : this indicates the event's tag.
|
29
|
+
# "part0" : this means if you indicate size_file then it will generate more parts if you file.size > size_file.
|
30
|
+
# When a file is full it will be pushed to the pool and will be deleted from the temporary directory.
|
31
|
+
# If a file is empty is not pushed, it is not deleted.
|
32
|
+
#
|
33
|
+
# This plugin have a system to restore the previous temporary files if something crash.
|
34
|
+
#
|
35
|
+
##[Note] :
|
36
|
+
#
|
37
|
+
## If you specify size_file and time_file then it will create file for each tag (if specified), when time_file or
|
38
|
+
## their size > size_file, it will be triggered then they will be pushed on Rados pool and will delete from local disk.
|
39
|
+
## If you don't specify size_file, but time_file then it will create only one file for each tag (if specified).
|
40
|
+
## When time_file it will be triggered then the files will be pushed on Rados and delete from local disk.
|
41
|
+
#
|
42
|
+
## If you don't specify time_file, but size_file then it will create files for each tag (if specified),
|
43
|
+
## that will be triggered when their size > size_file, then they will be pushed on Rados pool and will delete from local disk.
|
44
|
+
#
|
45
|
+
## If you don't specific size_file and time_file you have a curios mode. It will create only one file for each tag (if specified).
|
46
|
+
## Then the file will be rest on temporary directory and don't will be pushed on pool until we will restart logstash.
|
47
|
+
#
|
48
|
+
#
|
49
|
+
# #### Usage:
|
50
|
+
# This is an example of logstash config:
|
51
|
+
# [source,ruby]
|
52
|
+
# output {
|
53
|
+
# rados{
|
54
|
+
# mypool => "mypool" (required)
|
55
|
+
# size_file => 2048 (optional)
|
56
|
+
# time_file => 5 (optional)
|
57
|
+
# }
|
58
|
+
#
|
59
|
+
class LogStash::Outputs::Rados < LogStash::Outputs::Base
|
60
|
+
|
61
|
+
TEMPFILE_EXTENSION = "txt"
|
62
|
+
RADOS_INVALID_CHARACTERS = /[\^`><]/
|
63
|
+
|
64
|
+
|
65
|
+
config_name "rados"
|
66
|
+
default :codec, 'line'
|
67
|
+
|
68
|
+
# Rados pool
|
69
|
+
config :pool, :validate => :string, :default => 'logstash'
|
70
|
+
|
71
|
+
# Set the size of file in bytes, this means that files on pool when have dimension > file_size, they are stored in two or more file.
|
72
|
+
# If you have tags then it will generate a specific size file for every tags
|
73
|
+
##NOTE: define size of file is the better thing, because generate a local temporary file on disk and then put it in pool.
|
74
|
+
config :size_file, :validate => :number, :default => 0
|
75
|
+
|
76
|
+
# Set the time, in minutes, to close the current sub_time_section of pool.
|
77
|
+
# If you define file_size you have a number of files in consideration of the section and the current tag.
|
78
|
+
# 0 stay all time on listerner, beware if you specific 0 and size_file 0, because you will not put the file on pool,
|
79
|
+
# for now the only thing this plugin can do is to put the file when logstash restart.
|
80
|
+
config :time_file, :validate => :number, :default => 0
|
81
|
+
|
82
|
+
# Set the directory where logstash will store the tmp files before sending it to Rados
|
83
|
+
# default to the current OS temporary directory in linux /tmp/logstash
|
84
|
+
config :temporary_directory, :validate => :string, :default => File.join(Dir.tmpdir, "logstash")
|
85
|
+
|
86
|
+
# Specify a prefix to the uploaded filename, this can simulate directories on rados
|
87
|
+
config :prefix, :validate => :string, :default => ''
|
88
|
+
|
89
|
+
# Specify how many workers to use to upload the files to Rados
|
90
|
+
config :upload_workers_count, :validate => :number, :default => 1
|
91
|
+
|
92
|
+
# Define tags to be appended to the file on the Rados pool.
|
93
|
+
#
|
94
|
+
# Example:
|
95
|
+
# tags => ["elasticsearch", "logstash", "kibana"]
|
96
|
+
#
|
97
|
+
# Will generate this file:
|
98
|
+
# "ls.rados.logstash.local.2015-01-01T00.00.tag_elasticsearch.logstash.kibana.part0.txt"
|
99
|
+
#
|
100
|
+
config :tags, :validate => :array, :default => []
|
101
|
+
|
102
|
+
|
103
|
+
config :use_ssl, :validate => :boolean, :default => true
|
104
|
+
|
105
|
+
# Exposed attributes for testing purpose.
|
106
|
+
attr_accessor :tempfile
|
107
|
+
attr_reader :page_counter
|
108
|
+
# Exposed attributes for testing purpose.
|
109
|
+
attr_reader :cluster
|
110
|
+
|
111
|
+
|
112
|
+
public
|
113
|
+
def write_on_pool(file)
|
114
|
+
rados_pool = @cluster.pool(@pool)
|
115
|
+
rados_pool.open
|
116
|
+
remote_filename = "#{@prefix}#{File.basename(file)}"
|
117
|
+
|
118
|
+
@logger.debug("RADOS: ready to write file in pool", :remote_filename => remote_filename, :pool => @pool)
|
119
|
+
|
120
|
+
File.open(file, 'r') do |fileIO|
|
121
|
+
begin
|
122
|
+
# prepare for write the file
|
123
|
+
object = rados_pool.rados_object(remote_filename)
|
124
|
+
object.write(0, fileIO.read)
|
125
|
+
rescue SystemCallError => error
|
126
|
+
@logger.error("RADOS: CEPH error", :error => error)
|
127
|
+
raise LogStash::Error, "CEPH Configuration Error, #{error}"
|
128
|
+
ensure
|
129
|
+
rados_pool.close
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
@logger.debug("RADOS: has written remote file in pool", :remote_filename => remote_filename, :pool => @pool)
|
134
|
+
end
|
135
|
+
|
136
|
+
# This method is used for create new empty temporary files for use. Flag is needed for indicate new subsection time_file.
|
137
|
+
public
|
138
|
+
def create_temporary_file
|
139
|
+
filename = File.join(@temporary_directory, get_temporary_filename(@page_counter))
|
140
|
+
|
141
|
+
@logger.debug("RADOS: Creating a new temporary file", :filename => filename)
|
142
|
+
|
143
|
+
@file_rotation_lock.synchronize do
|
144
|
+
unless @tempfile.nil?
|
145
|
+
@tempfile.close
|
146
|
+
end
|
147
|
+
|
148
|
+
@tempfile = File.open(filename, "a")
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
public
|
153
|
+
def register
|
154
|
+
require "ceph-ruby"
|
155
|
+
# required if using ruby version < 2.0
|
156
|
+
# http://ruby.awsblog.com/post/Tx16QY1CI5GVBFT/Threading-with-the-AWS-SDK-for-Ruby
|
157
|
+
workers_not_supported
|
158
|
+
|
159
|
+
@cluster = CephRuby::Cluster.new
|
160
|
+
@upload_queue = Queue.new
|
161
|
+
@file_rotation_lock = Mutex.new
|
162
|
+
|
163
|
+
if @prefix && @prefix =~ RADOS_INVALID_CHARACTERS
|
164
|
+
@logger.error("RADOS: prefix contains invalid characters", :prefix => @prefix, :contains => RADOS_INVALID_CHARACTERS)
|
165
|
+
raise LogStash::ConfigurationError, "RADOS: prefix contains invalid characters"
|
166
|
+
end
|
167
|
+
|
168
|
+
if !Dir.exist?(@temporary_directory)
|
169
|
+
FileUtils.mkdir_p(@temporary_directory)
|
170
|
+
end
|
171
|
+
restore_from_crashes if @restore == true
|
172
|
+
reset_page_counter
|
173
|
+
create_temporary_file
|
174
|
+
configure_periodic_rotation if time_file != 0
|
175
|
+
configure_upload_workers
|
176
|
+
|
177
|
+
@codec.on_event do |event, encoded_event|
|
178
|
+
handle_event(encoded_event)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
public
|
183
|
+
def restore_from_crashes
|
184
|
+
@logger.debug("RADOS: is attempting to verify previous crashes...")
|
185
|
+
|
186
|
+
Dir[File.join(@temporary_directory, "*.#{TEMPFILE_EXTENSION}")].each do |file|
|
187
|
+
name_file = File.basename(file)
|
188
|
+
@logger.warn("RADOS: have found temporary file the upload process crashed, uploading file to Rados.", :filename => name_file)
|
189
|
+
move_file_to_pool_async(file)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
public
|
194
|
+
def move_file_to_pool(file)
|
195
|
+
if !File.zero?(file)
|
196
|
+
write_on_pool(file)
|
197
|
+
@logger.debug("RADOS: file was put on the upload thread", :filename => File.basename(file), :pool => @pool)
|
198
|
+
end
|
199
|
+
|
200
|
+
begin
|
201
|
+
File.delete(file)
|
202
|
+
rescue Errno::ENOENT
|
203
|
+
# Something else deleted the file, logging but not raising the issue
|
204
|
+
@logger.warn("RADOS: Cannot delete the temporary file since it doesn't exist on disk", :filename => File.basename(file))
|
205
|
+
rescue Errno::EACCES
|
206
|
+
@logger.error("RADOS: Logstash doesnt have the permission to delete the file in the temporary directory.", :filename => File.basename(file), :temporary_directory => @temporary_directory)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
public
|
211
|
+
def periodic_interval
|
212
|
+
@time_file * 60
|
213
|
+
end
|
214
|
+
|
215
|
+
public
|
216
|
+
def get_temporary_filename(page_counter = 0)
|
217
|
+
current_time = Time.now
|
218
|
+
filename = "ls.rados.#{Socket.gethostname}.#{current_time.strftime("%Y-%m-%dT%H.%M")}"
|
219
|
+
|
220
|
+
if @tags.size > 0
|
221
|
+
return "#{filename}.tag_#{@tags.join('.')}.part#{page_counter}.#{TEMPFILE_EXTENSION}"
|
222
|
+
else
|
223
|
+
return "#{filename}.part#{page_counter}.#{TEMPFILE_EXTENSION}"
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
public
|
228
|
+
def receive(event)
|
229
|
+
|
230
|
+
@codec.encode(event)
|
231
|
+
end
|
232
|
+
|
233
|
+
public
|
234
|
+
def rotate_events_log?
|
235
|
+
@file_rotation_lock.synchronize do
|
236
|
+
@tempfile.size > @size_file
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
public
|
241
|
+
def write_events_to_multiple_files?
|
242
|
+
@size_file > 0
|
243
|
+
end
|
244
|
+
|
245
|
+
public
|
246
|
+
def write_to_tempfile(event)
|
247
|
+
begin
|
248
|
+
@logger.debug("RADOS: put event into tempfile ", :tempfile => File.basename(@tempfile))
|
249
|
+
|
250
|
+
@file_rotation_lock.synchronize do
|
251
|
+
@tempfile.syswrite(event)
|
252
|
+
end
|
253
|
+
rescue Errno::ENOSPC
|
254
|
+
@logger.error("RADOS: No space left in temporary directory", :temporary_directory => @temporary_directory)
|
255
|
+
close
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
public # Specify how many workers to use to upload the files to Rados
|
260
|
+
config :upload_workers_count, :validate => :number, :default => 1
|
261
|
+
def close
|
262
|
+
shutdown_upload_workers
|
263
|
+
@periodic_rotation_thread.stop! if @periodic_rotation_thread
|
264
|
+
|
265
|
+
@file_rotation_lock.synchronize do
|
266
|
+
@tempfile.close unless @tempfile.nil? && @tempfile.closed?
|
267
|
+
end
|
268
|
+
@cluster.shutdown
|
269
|
+
end
|
270
|
+
|
271
|
+
private
|
272
|
+
def shutdown_upload_workers
|
273
|
+
@logger.debug("RADOS: Gracefully shutdown the upload workers")
|
274
|
+
@upload_queue << LogStash::ShutdownEvent
|
275
|
+
end
|
276
|
+
|
277
|
+
private
|
278
|
+
def handle_event(encoded_event)
|
279
|
+
if write_events_to_multiple_files?
|
280
|
+
if rotate_events_log?
|
281
|
+
@logger.debug("RADOS: tempfile is too large, let's upload it and create new file", :tempfile => File.basename(@tempfile))
|
282
|
+
|
283
|
+
move_file_to_pool_async(@tempfile.path)
|
284
|
+
next_page
|
285
|
+
create_temporary_file
|
286
|
+
else
|
287
|
+
@logger.debug("RADOS: tempfile file size report.", :tempfile_size => @tempfile.size, :size_file => @size_file)
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
write_to_tempfile(encoded_event)
|
292
|
+
end
|
293
|
+
|
294
|
+
private
|
295
|
+
def configure_periodic_rotation
|
296
|
+
@periodic_rotation_thread = Stud::Task.new do
|
297
|
+
LogStash::Util::set_thread_name("<RADOS periodic uploader")
|
298
|
+
|
299
|
+
Stud.interval(periodic_interval, :sleep_then_run => true) do
|
300
|
+
@logger.debug("RADOS: time_file triggered, uploading the file", :filename => @tempfile.path)
|
301
|
+
|
302
|
+
move_file_to_pool_async(@tempfile.path)
|
303
|
+
next_page
|
304
|
+
create_temporary_file
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
private
|
310
|
+
def configure_upload_workers
|
311
|
+
@logger.debug("RADOS: Configure upload workers")
|
312
|
+
|
313
|
+
@upload_workers = @upload_workers_count.times.map do |worker_id|
|
314
|
+
Stud::Task.new do
|
315
|
+
LogStash::Util::set_thread_name("<RADOS upload worker #{worker_id}")
|
316
|
+
|
317
|
+
while true do
|
318
|
+
@logger.debug("RADOS: upload worker is waiting for a new file to upload.", :worker_id => worker_id)
|
319
|
+
|
320
|
+
upload_worker
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
private
|
327
|
+
def upload_worker
|
328
|
+
file = @upload_queue.deq
|
329
|
+
|
330
|
+
case file
|
331
|
+
when LogStash::ShutdownEvent
|
332
|
+
@logger.debug("RADOS: upload worker is shutting down gracefuly")
|
333
|
+
@upload_queue.enq(LogStash::ShutdownEvent)
|
334
|
+
else
|
335
|
+
@logger.debug("RADOS: upload worker is uploading a new file", :filename => File.basename(file))
|
336
|
+
move_file_to_pool(file)
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
private
|
341
|
+
def next_page
|
342
|
+
@page_counter += 1
|
343
|
+
end
|
344
|
+
|
345
|
+
private
|
346
|
+
def reset_page_counter
|
347
|
+
@page_counter = 0
|
348
|
+
end
|
349
|
+
|
350
|
+
private
|
351
|
+
def delete_on_pool(filename)
|
352
|
+
rados_pool = @cluster.pool(@pool)
|
353
|
+
rados_pool.open
|
354
|
+
remote_filename = "#{@prefix}#{File.basename(filename)}"
|
355
|
+
|
356
|
+
@logger.debug("RADOS: delete file from pool", :remote_filename => remote_filename, :pool => @pool)
|
357
|
+
|
358
|
+
begin
|
359
|
+
object = rados_pool.rados_object(remote_filename)
|
360
|
+
object.destroy
|
361
|
+
rescue SystemCallError => error
|
362
|
+
@logger.error("RADOS: CEPH error", :error => error)
|
363
|
+
raise LogStash::Error, "CEPH Configuration Error, #{error}"
|
364
|
+
ensure
|
365
|
+
rados_pool.close
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
private
|
370
|
+
def move_file_to_pool_async(file)
|
371
|
+
@logger.debug("RADOS: Sending the file to the upload queue.", :filename => File.basename(file))
|
372
|
+
@upload_queue.enq(file)
|
373
|
+
end
|
374
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
|
3
|
+
s.name = 'logstash-output-rados'
|
4
|
+
s.version = '1.0.0'
|
5
|
+
s.licenses = ['Apache License (2.0)']
|
6
|
+
s.summary = "This plugin was created for store the logstash's events into Ceph's Rados"
|
7
|
+
s.description = "This gem is a logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/plugin install gemname. This gem is not a stand-alone program"
|
8
|
+
s.authors = ["Deathowl"]
|
9
|
+
s.email = 'bagoly@endticket.com'
|
10
|
+
s.homepage = "http://www.elastic.co/guide/en/logstash/current/index.html"
|
11
|
+
s.require_paths = ["lib"]
|
12
|
+
|
13
|
+
# Files
|
14
|
+
s.files = Dir['lib/**/*','spec/**/*','vendor/**/*','*.gemspec','*.md','CONTRIBUTORS','Gemfile','LICENSE','NOTICE.TXT']
|
15
|
+
|
16
|
+
# Tests
|
17
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
18
|
+
|
19
|
+
# Special flag to let us know this is actually a logstash plugin
|
20
|
+
s.metadata = { "logstash_plugin" => "true", "logstash_group" => "output" }
|
21
|
+
|
22
|
+
# Gem dependencies
|
23
|
+
s.add_runtime_dependency "logstash-core", ">= 2.0.0.beta2", "< 3.0.0"
|
24
|
+
s.add_runtime_dependency 'stud', '~> 0.0.22'
|
25
|
+
s.add_runtime_dependency 'ceph-ruby', '~> 1.1'
|
26
|
+
s.add_development_dependency 'logstash-devutils'
|
27
|
+
s.add_development_dependency 'logstash-input-generator'
|
28
|
+
s.add_development_dependency 'logstash-codec-line'
|
29
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require "logstash/devutils/rspec/spec_helper"
|
2
|
+
require "logstash/outputs/rados"
|
3
|
+
require 'socket'
|
4
|
+
require "fileutils"
|
5
|
+
require "stud/temporary"
|
6
|
+
require_relative "../supports/helpers"
|
7
|
+
|
8
|
+
describe LogStash::Outputs::Rados, :integration => true, :rados => true do
|
9
|
+
before do
|
10
|
+
Thread.abort_on_exception = true
|
11
|
+
end
|
12
|
+
|
13
|
+
let!(:minimal_settings) { { "pool" => ENV['RADOS_LOGSTASH_TEST_POOL'],
|
14
|
+
"temporary_directory" => Stud::Temporary.pathname('temporary_directory') }}
|
15
|
+
|
16
|
+
let!(:rados_object) do
|
17
|
+
radosoutput = LogStash::Outputs::Rados.new(minimal_settings)
|
18
|
+
radosoutput.register
|
19
|
+
radosoutput.cluster
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#register" do
|
23
|
+
it "write a file on the pool to check permissions" do
|
24
|
+
rados = LogStash::Outputs::Rados.new(minimal_settings)
|
25
|
+
expect(rados.register).not_to raise_error
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "#write_on_pool" do
|
30
|
+
after(:each) do
|
31
|
+
File.unlink(fake_data.path)
|
32
|
+
end
|
33
|
+
|
34
|
+
let!(:fake_data) { Stud::Temporary.file }
|
35
|
+
|
36
|
+
it "should prefix the file on the pool if a prefix is specified" do
|
37
|
+
prefix = "my-prefix"
|
38
|
+
|
39
|
+
config = minimal_settings.merge({
|
40
|
+
"prefix" => prefix,
|
41
|
+
})
|
42
|
+
|
43
|
+
rados = LogStash::Outputs::Rados.new(config)
|
44
|
+
rados.register
|
45
|
+
rados.write_on_pool(fake_data)
|
46
|
+
|
47
|
+
expect(key_exists_on_pool?("#{prefix}#{File.basename(fake_data.path)}")).to eq(true)
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should use the same local filename if no prefix is specified' do
|
51
|
+
rados = LogStash::Outputs::Rados.new(minimal_settings)
|
52
|
+
rados.register
|
53
|
+
rados.write_on_pool(fake_data)
|
54
|
+
|
55
|
+
expect(key_exists_on_pool?(File.basename(fake_data.path))).to eq(true)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "#move_file_to_pool" do
|
60
|
+
let!(:rados) { LogStash::Outputs::Rados.new(minimal_settings) }
|
61
|
+
|
62
|
+
before do
|
63
|
+
rados.register
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should upload the file if the size > 0" do
|
67
|
+
tmp = Stud::Temporary.file
|
68
|
+
allow(File).to receive(:zero?).and_return(false)
|
69
|
+
rados.move_file_to_pool(tmp)
|
70
|
+
expect(key_exists_on_pool?(File.basename(tmp.path))).to eq(true)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe "#restore_from_crashes" do
|
75
|
+
it "read the temp directory and upload the matching file to rados" do
|
76
|
+
Stud::Temporary.pathname do |temp_path|
|
77
|
+
tempfile = File.open(File.join(temp_path, 'A'), 'w+') { |f| f.write('test')}
|
78
|
+
|
79
|
+
rados = LogStash::Outputs::Rados.new(minimal_settings.merge({ "temporary_directory" => temp_path }))
|
80
|
+
rados.restore_from_crashes
|
81
|
+
|
82
|
+
expect(File.exist?(tempfile.path)).to eq(false)
|
83
|
+
expect(key_exists_on_pool?(File.basename(tempfile.path))).to eq(true)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,279 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "logstash/devutils/rspec/spec_helper"
|
3
|
+
require "logstash/outputs/rados"
|
4
|
+
require "logstash/codecs/line"
|
5
|
+
require "logstash/pipeline"
|
6
|
+
require "fileutils"
|
7
|
+
require_relative "../supports/helpers"
|
8
|
+
|
9
|
+
describe LogStash::Outputs::Rados do
|
10
|
+
before do
|
11
|
+
# We stub all the calls from Rad, for more information see:
|
12
|
+
# http://ruby.awsblog.com/post/Tx2SU6TYJWQQLC3/Stubbing-AWS-Responses
|
13
|
+
Thread.abort_on_exception = true
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:minimal_settings) { {"pool" => ENV['RADOS_LOGSTASH_TEST_POOL'] } }
|
17
|
+
|
18
|
+
describe "#register" do
|
19
|
+
it "should create the tmp directory if it doesn't exist" do
|
20
|
+
temporary_directory = Stud::Temporary.pathname("temporary_directory")
|
21
|
+
|
22
|
+
config = {
|
23
|
+
"pool" => "logstash",
|
24
|
+
"size_file" => 10,
|
25
|
+
"temporary_directory" => temporary_directory
|
26
|
+
}
|
27
|
+
|
28
|
+
rados = LogStash::Outputs::Rados.new(config)
|
29
|
+
rados.register
|
30
|
+
|
31
|
+
expect(Dir.exist?(temporary_directory)).to eq(true)
|
32
|
+
rados.close
|
33
|
+
FileUtils.rm_r(temporary_directory)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should raise a ConfigurationError if the prefix contains one or more '\^`><' characters" do
|
37
|
+
config = {
|
38
|
+
"prefix" => "`no\><^"
|
39
|
+
}
|
40
|
+
|
41
|
+
rados = LogStash::Outputs::Rados.new(config)
|
42
|
+
|
43
|
+
expect {
|
44
|
+
rados.register
|
45
|
+
}.to raise_error(LogStash::ConfigurationError)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "#generate_temporary_filename" do
|
50
|
+
before do
|
51
|
+
allow(Socket).to receive(:gethostname) { "logstash.local" }
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should add tags to the filename if present" do
|
55
|
+
config = minimal_settings.merge({ "tags" => ["elasticsearch", "logstash", "kibana"], "temporary_directory" => "/tmp/logstash"})
|
56
|
+
rados = LogStash::Outputs::Rados.new(config)
|
57
|
+
expect(rados.get_temporary_filename).to match(/^ls\.rados\.logstash\.local\.\d{4}-\d{2}\-\d{2}T\d{2}\.\d{2}\.tag_#{config["tags"].join("\.")}\.part0\.txt\Z/)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should not add the tags to the filename" do
|
61
|
+
config = minimal_settings.merge({ "tags" => [], "temporary_directory" => "/tmp/logstash" })
|
62
|
+
rados = LogStash::Outputs::Rados.new(config)
|
63
|
+
expect(rados.get_temporary_filename(3)).to match(/^ls\.rados\.logstash\.local\.\d{4}-\d{2}\-\d{2}T\d{2}\.\d{2}\.part3\.txt\Z/)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "normalized the temp directory to include the trailing slash if missing" do
|
67
|
+
rados = LogStash::Outputs::Rados.new(minimal_settings.merge({ "temporary_directory" => "/tmp/logstash" }))
|
68
|
+
expect(rados.get_temporary_filename).to match(/^ls\.rados\.logstash\.local\.\d{4}-\d{2}\-\d{2}T\d{2}\.\d{2}\.part0\.txt\Z/)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
describe "#write_events_to_multiple_files?" do
|
74
|
+
it 'returns true if the size_file is != 0 ' do
|
75
|
+
rados = LogStash::Outputs::Rados.new(minimal_settings.merge({ "size_file" => 200 }))
|
76
|
+
expect(rados.write_events_to_multiple_files?).to eq(true)
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'returns false if size_file is zero or not set' do
|
80
|
+
rados = LogStash::Outputs::Rados.new(minimal_settings)
|
81
|
+
expect(rados.write_events_to_multiple_files?).to eq(false)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe "#write_to_tempfile" do
|
86
|
+
it "should append the event to a file" do
|
87
|
+
Stud::Temporary.file("logstash", "a+") do |tmp|
|
88
|
+
rados = LogStash::Outputs::Rados.new(minimal_settings)
|
89
|
+
rados.register
|
90
|
+
rados.tempfile = tmp
|
91
|
+
rados.write_to_tempfile("test-write")
|
92
|
+
tmp.rewind
|
93
|
+
expect(tmp.read).to eq("test-write")
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe "#rotate_events_log" do
|
99
|
+
|
100
|
+
context "having a single worker" do
|
101
|
+
let(:rados) { LogStash::Outputs::Rados.new(minimal_settings.merge({ "size_file" => 1024 })) }
|
102
|
+
|
103
|
+
before(:each) do
|
104
|
+
rados.register
|
105
|
+
end
|
106
|
+
|
107
|
+
it "returns true if the tempfile is over the file_size limit" do
|
108
|
+
Stud::Temporary.file do |tmp|
|
109
|
+
allow(tmp).to receive(:size) { 2024001 }
|
110
|
+
|
111
|
+
rados.tempfile = tmp
|
112
|
+
expect(rados.rotate_events_log?).to be(true)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
it "returns false if the tempfile is under the file_size limit" do
|
117
|
+
Stud::Temporary.file do |tmp|
|
118
|
+
allow(tmp).to receive(:size) { 100 }
|
119
|
+
|
120
|
+
rados.tempfile = tmp
|
121
|
+
expect(rados.rotate_events_log?).to eq(false)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context "having periodic rotations" do
|
127
|
+
let(:rados) { LogStash::Outputs::Rados.new(minimal_settings.merge({ "size_file" => 1024, "time_file" => 6e-10 })) }
|
128
|
+
let(:tmp) { Tempfile.new('rados_rotation_temp_file') }
|
129
|
+
|
130
|
+
before(:each) do
|
131
|
+
rados.tempfile = tmp
|
132
|
+
rados.register
|
133
|
+
end
|
134
|
+
|
135
|
+
after(:each) do
|
136
|
+
rados.close
|
137
|
+
tmp.close
|
138
|
+
tmp.unlink
|
139
|
+
end
|
140
|
+
|
141
|
+
it "raises no error when periodic rotation happen" do
|
142
|
+
1000.times do
|
143
|
+
expect { rados.rotate_events_log? }.not_to raise_error
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
describe "#move_file_to_pool" do
|
150
|
+
subject { LogStash::Outputs::Rados.new(minimal_settings) }
|
151
|
+
|
152
|
+
it "should always delete the source file" do
|
153
|
+
tmp = Stud::Temporary.file
|
154
|
+
|
155
|
+
allow(File).to receive(:zero?).and_return(true)
|
156
|
+
expect(File).to receive(:delete).with(tmp)
|
157
|
+
|
158
|
+
subject.move_file_to_pool(tmp)
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'should not upload the file if the size of the file is zero' do
|
162
|
+
temp_file = Stud::Temporary.file
|
163
|
+
allow(temp_file).to receive(:zero?).and_return(true)
|
164
|
+
|
165
|
+
expect(subject).not_to receive(:write_on_pool)
|
166
|
+
subject.move_file_to_pool(temp_file)
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should upload the file if the size > 0" do
|
170
|
+
tmp = Stud::Temporary.file
|
171
|
+
|
172
|
+
allow(File).to receive(:zero?).and_return(false)
|
173
|
+
expect(subject).to receive(:write_on_pool)
|
174
|
+
|
175
|
+
subject.move_file_to_pool(tmp)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
describe "#restore_from_crashes" do
|
180
|
+
it "read the temp directory and upload the matching file to rados" do
|
181
|
+
subject = LogStash::Outputs::Rados.new(minimal_settings.merge({ "temporary_directory" => "/tmp/logstash/" }))
|
182
|
+
|
183
|
+
expect(Dir).to receive(:[]).with("/tmp/logstash/*.txt").and_return(["/tmp/logstash/01.txt"])
|
184
|
+
expect(subject).to receive(:move_file_to_pool_async).with("/tmp/logstash/01.txt")
|
185
|
+
|
186
|
+
|
187
|
+
subject.restore_from_crashes
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
describe "#receive" do
|
192
|
+
it "should send the event through the codecs" do
|
193
|
+
data = {"foo" => "bar", "baz" => {"bah" => ["a","b","c"]}, "@timestamp" => "2014-05-30T02:52:17.929Z"}
|
194
|
+
event = LogStash::Event.new(data)
|
195
|
+
|
196
|
+
expect_any_instance_of(LogStash::Codecs::Line).to receive(:encode).with(event)
|
197
|
+
|
198
|
+
subject = LogStash::Outputs::Rados.new(minimal_settings)
|
199
|
+
subject.register
|
200
|
+
|
201
|
+
subject.receive(event)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
describe "when rotating the temporary file" do
|
206
|
+
before { allow(File).to receive(:delete) }
|
207
|
+
|
208
|
+
it "doesn't skip events if using the size_file option" do
|
209
|
+
Stud::Temporary.directory do |temporary_directory|
|
210
|
+
size_file = rand(200..20000)
|
211
|
+
event_count = rand(300..15000)
|
212
|
+
|
213
|
+
config = %Q[
|
214
|
+
input {
|
215
|
+
generator {
|
216
|
+
count => #{event_count}
|
217
|
+
}
|
218
|
+
}
|
219
|
+
output {
|
220
|
+
rados {
|
221
|
+
size_file => #{size_file}
|
222
|
+
codec => line
|
223
|
+
temporary_directory => '#{temporary_directory}'
|
224
|
+
pool => '#{ENV['RADOS_LOGSTASH_TEST_POOL']}'
|
225
|
+
}
|
226
|
+
}
|
227
|
+
]
|
228
|
+
|
229
|
+
pipeline = LogStash::Pipeline.new(config)
|
230
|
+
|
231
|
+
pipeline_thread = Thread.new { pipeline.run }
|
232
|
+
sleep 0.1 while !pipeline.ready?
|
233
|
+
pipeline_thread.join
|
234
|
+
|
235
|
+
events_written_count = events_in_files(Dir[File.join(temporary_directory, 'ls.*.txt')])
|
236
|
+
expect(events_written_count).to eq(event_count)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
it "doesn't skip events if using the time_file option", :tag => :slow do
|
241
|
+
Stud::Temporary.directory do |temporary_directory|
|
242
|
+
time_file = rand(1..2)
|
243
|
+
number_of_rotation = rand(2..5)
|
244
|
+
|
245
|
+
config = {
|
246
|
+
"time_file" => time_file,
|
247
|
+
"codec" => "line",
|
248
|
+
"temporary_directory" => temporary_directory,
|
249
|
+
"pool" => "#{ENV['RADOS_LOGSTASH_TEST_POOL']}"
|
250
|
+
}
|
251
|
+
|
252
|
+
rados = LogStash::Outputs::Rados.new(minimal_settings.merge(config))
|
253
|
+
# Make the test run in seconds intead of minutes..
|
254
|
+
expect(rados).to receive(:periodic_interval).and_return(time_file)
|
255
|
+
rados.register
|
256
|
+
|
257
|
+
# Force to have a few files rotation
|
258
|
+
stop_time = Time.now + (number_of_rotation * time_file)
|
259
|
+
event_count = 0
|
260
|
+
|
261
|
+
event = LogStash::Event.new("message" => "Hello World")
|
262
|
+
|
263
|
+
until Time.now > stop_time do
|
264
|
+
rados.receive(event)
|
265
|
+
event_count += 1
|
266
|
+
end
|
267
|
+
rados.close
|
268
|
+
|
269
|
+
generated_files = Dir[File.join(temporary_directory, 'ls.*.txt')]
|
270
|
+
|
271
|
+
events_written_count = events_in_files(generated_files)
|
272
|
+
|
273
|
+
# Skew times can affect the number of rotation..
|
274
|
+
expect(generated_files.count).to be_within(number_of_rotation).of(number_of_rotation + 1)
|
275
|
+
expect(events_written_count).to eq(event_count)
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|
metadata
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: logstash-output-rados
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Deathowl
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-01-29 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.0.beta2
|
19
|
+
- - <
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 3.0.0
|
22
|
+
name: logstash-core
|
23
|
+
prerelease: false
|
24
|
+
type: :runtime
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 2.0.0.beta2
|
30
|
+
- - <
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 3.0.0
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
requirement: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - ~>
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: 0.0.22
|
39
|
+
name: stud
|
40
|
+
prerelease: false
|
41
|
+
type: :runtime
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ~>
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 0.0.22
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ~>
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '1.1'
|
53
|
+
name: ceph-ruby
|
54
|
+
prerelease: false
|
55
|
+
type: :runtime
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ~>
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '1.1'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
requirement: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - '>='
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
name: logstash-devutils
|
68
|
+
prerelease: false
|
69
|
+
type: :development
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - '>='
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
requirement: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - '>='
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
name: logstash-input-generator
|
82
|
+
prerelease: false
|
83
|
+
type: :development
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - '>='
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
requirement: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
name: logstash-codec-line
|
96
|
+
prerelease: false
|
97
|
+
type: :development
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - '>='
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
description: This gem is a logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/plugin install gemname. This gem is not a stand-alone program
|
104
|
+
email: bagoly@endticket.com
|
105
|
+
executables: []
|
106
|
+
extensions: []
|
107
|
+
extra_rdoc_files: []
|
108
|
+
files:
|
109
|
+
- CHANGELOG.md
|
110
|
+
- CONTRIBUTORS
|
111
|
+
- DEVELOPER.md
|
112
|
+
- Gemfile
|
113
|
+
- LICENSE
|
114
|
+
- NOTICE.TXT
|
115
|
+
- README.md
|
116
|
+
- lib/logstash/outputs/rados.rb
|
117
|
+
- logstash-output-rados.gemspec
|
118
|
+
- spec/integration/rados_spec.rb
|
119
|
+
- spec/outputs/rados_spec.rb
|
120
|
+
- spec/supports/helpers.rb
|
121
|
+
homepage: http://www.elastic.co/guide/en/logstash/current/index.html
|
122
|
+
licenses:
|
123
|
+
- Apache License (2.0)
|
124
|
+
metadata:
|
125
|
+
logstash_plugin: 'true'
|
126
|
+
logstash_group: output
|
127
|
+
post_install_message:
|
128
|
+
rdoc_options: []
|
129
|
+
require_paths:
|
130
|
+
- lib
|
131
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
132
|
+
requirements:
|
133
|
+
- - '>='
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: '0'
|
136
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
137
|
+
requirements:
|
138
|
+
- - '>='
|
139
|
+
- !ruby/object:Gem::Version
|
140
|
+
version: '0'
|
141
|
+
requirements: []
|
142
|
+
rubyforge_project:
|
143
|
+
rubygems_version: 2.4.5
|
144
|
+
signing_key:
|
145
|
+
specification_version: 4
|
146
|
+
summary: This plugin was created for store the logstash's events into Ceph's Rados
|
147
|
+
test_files:
|
148
|
+
- spec/integration/rados_spec.rb
|
149
|
+
- spec/outputs/rados_spec.rb
|
150
|
+
- spec/supports/helpers.rb
|