logstash-output-harbor_beacon 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 12a689753ad344a88d1c675b3d3bbd4424b6e6aa054b9e9a4baf9db4d6cbb7ee
4
+ data.tar.gz: d5cc214c27a3b2b01f07339a2ebd824c3c0d4a7b3f3b1ef2a2e4097869e7c270
5
+ SHA512:
6
+ metadata.gz: 9452adf6b1d1c049f24974a1d62f9b9e662ecf479309f27db9beb0ba2c6b11ac9e14bf8445254120606288aa04fb773975510ed07d922ab417a14459a7a315b8
7
+ data.tar.gz: 1e980c6f23eef6be5bd1eb1799d58cea358f7a7927a1ff8d5367f53323c32a8cc684bfbeacbba6ea04e0b23434d02c527ff4a01fa58c27847c03dd591e8b29a0
@@ -0,0 +1,2 @@
1
+ ## 0.1.0
2
+ - Plugin created with the logstash plugin generator
@@ -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
+ * Scott Matheson - scott@hrbr.io
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/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+
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.
@@ -0,0 +1,86 @@
1
+ # Logstash Plugin
2
+
3
+ This is a plugin for [Logstash](https://github.com/elastic/logstash).
4
+
5
+ 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.
6
+
7
+ ## Documentation
8
+
9
+ 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/).
10
+
11
+ - For formatting code or config example, you can use the asciidoc `[source,ruby]` directive
12
+ - For more asciidoc formatting tips, see the excellent reference here https://github.com/elastic/docs#asciidoc-guide
13
+
14
+ ## Need Help?
15
+
16
+ Need help? Try #logstash on freenode IRC or the https://discuss.elastic.co/c/logstash discussion forum.
17
+
18
+ ## Developing
19
+
20
+ ### 1. Plugin Developement and Testing
21
+
22
+ #### Code
23
+ - To get started, you'll need JRuby with the Bundler gem installed.
24
+
25
+ - 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).
26
+
27
+ - Install dependencies
28
+ ```sh
29
+ bundle install
30
+ ```
31
+
32
+ #### Test
33
+
34
+ - Update your dependencies
35
+
36
+ ```sh
37
+ bundle install
38
+ ```
39
+
40
+ - Run tests
41
+
42
+ ```sh
43
+ bundle exec rspec
44
+ ```
45
+
46
+ ### 2. Running your unpublished Plugin in Logstash
47
+
48
+ #### 2.1 Run in a local Logstash clone
49
+
50
+ - Edit Logstash `Gemfile` and add the local plugin path, for example:
51
+ ```ruby
52
+ gem "logstash-filter-awesome", :path => "/your/local/logstash-filter-awesome"
53
+ ```
54
+ - Install plugin
55
+ ```sh
56
+ bin/logstash-plugin install --no-verify
57
+ ```
58
+ - Run Logstash with your plugin
59
+ ```sh
60
+ bin/logstash -e 'filter {awesome {}}'
61
+ ```
62
+ At this point any modifications to the plugin code will be applied to this local Logstash setup. After modifying the plugin, simply rerun Logstash.
63
+
64
+ #### 2.2 Run in an installed Logstash
65
+
66
+ 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:
67
+
68
+ - Build your plugin gem
69
+ ```sh
70
+ gem build logstash-filter-awesome.gemspec
71
+ ```
72
+ - Install the plugin from the Logstash home
73
+ ```sh
74
+ bin/logstash-plugin install /your/local/plugin/logstash-filter-awesome.gem
75
+ ```
76
+ - Start Logstash and proceed to test the plugin
77
+
78
+ ## Contributing
79
+
80
+ All contributions are welcome: ideas, patches, documentation, bug reports, complaints, and even something you drew up on a napkin.
81
+
82
+ 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.
83
+
84
+ It is more important to the community that you are able to contribute.
85
+
86
+ For more information about contributing, see the [CONTRIBUTING](https://github.com/elastic/logstash/blob/master/CONTRIBUTING.md) file.
@@ -0,0 +1,422 @@
1
+ # encoding: utf-8
2
+ require "logstash/outputs/base"
3
+ require "logstash/namespace"
4
+ require "logstash/json"
5
+ require "uri"
6
+ require "logstash/plugin_mixins/http_client"
7
+ require "zlib"
8
+
9
+ # An harbor_beacon output that does nothing.
10
+ class LogStash::Outputs::HarborBeacon < LogStash::Outputs::Base
11
+ include LogStash::PluginMixins::HttpClient
12
+
13
+ concurrency :shared
14
+
15
+ attr_accessor :is_batch
16
+
17
+ RETRYABLE_MANTICORE_EXCEPTIONS = [
18
+ ::Manticore::Timeout,
19
+ ::Manticore::SocketException,
20
+ ::Manticore::ClientProtocolException,
21
+ ::Manticore::ResolutionFailure,
22
+ ::Manticore::SocketTimeout
23
+ ]
24
+
25
+ # This output lets you send events to a
26
+ # generic HTTP(S) endpoint
27
+ #
28
+ # This output will execute up to 'pool_max' requests in parallel for performance.
29
+ # Consider this when tuning this plugin for performance.
30
+ #
31
+ # Additionally, note that when parallel execution is used strict ordering of events is not
32
+ # guaranteed!
33
+ #
34
+ # Beware, this gem does not yet support codecs. Please use the 'format' option for now.
35
+
36
+ config_name "harbor_beacon"
37
+
38
+ # URL to use
39
+ @@url = "https://harbor-stream.hrbr.io/beacon"
40
+
41
+ # The HTTP Verb. Must be "post"
42
+ @@http_method = "post"
43
+
44
+ # Content type
45
+ @@content_type = "application/json"
46
+
47
+
48
+ # Custom headers to use
49
+ # format is `headers => ["X-My-Header", "%{host}"]`
50
+ config :headers, :validate => :hash, :default => {}
51
+
52
+ # API Key
53
+ config :api_key, :validate => :string, :required => :true
54
+
55
+ # App Version Id
56
+ config :app_version_id, :validate => :string, :required => :true
57
+
58
+ # Beacon Version Id
59
+ config :beacon_version_id, :validate => :string, :required => :true
60
+
61
+ # Beacon Message Type
62
+ config :beacon_message_type, :validate => :string, :required => :true
63
+
64
+ # Beacon Instance ID
65
+ config :beacon_instance_id, :validate => :string
66
+
67
+ # Data Timestamp
68
+ config :data_timestamp, :validate => :string
69
+
70
+ # Set this to false if you don't want this output to retry failed requests
71
+ config :retry_failed, :validate => :boolean, :default => true
72
+
73
+ # If encountered as response codes this plugin will retry these requests
74
+ config :retryable_codes, :validate => :number, :list => true, :default => [429, 500, 502, 503, 504]
75
+
76
+ # If you would like to consider some non-2xx codes to be successes
77
+ # enumerate them here. Responses returning these codes will be considered successes
78
+ config :ignorable_codes, :validate => :number, :list => true
79
+
80
+ # This lets you choose the structure and parts of the event that are sent.
81
+ #
82
+ #
83
+ # For example:
84
+ # [source,ruby]
85
+ # mapping => {"foo" => "%{host}"
86
+ # "bar" => "%{type}"}
87
+ config :mapping, :validate => :hash
88
+
89
+ # Set the format of the http body.
90
+ #
91
+ # If message, then the body will be the result of formatting the event according to message
92
+ #
93
+ # Otherwise, the event is sent as json.
94
+ config :format, :validate => ["json", "message"], :default => "json"
95
+
96
+ # Set this to true if you want to enable gzip compression for your http requests
97
+ #
98
+ # Compression is not supported at this time
99
+ #
100
+ # config :http_compression, :validate => :boolean, :default => false
101
+
102
+ config :message, :validate => :string
103
+
104
+ public
105
+ def register
106
+
107
+ # We count outstanding requests with this queue
108
+ # This queue tracks the requests to create backpressure
109
+ # When this queue is empty no new requests may be sent,
110
+ # tokens must be added back by the client on success
111
+ @request_tokens = SizedQueue.new(@pool_max)
112
+ @pool_max.times {|t| @request_tokens << true }
113
+
114
+ @requests = Array.new
115
+
116
+ # Compression is not supported at this time
117
+ #
118
+ @http_compression = false
119
+
120
+ @is_batch = false
121
+
122
+ @headers["Content-Type"] = @@content_type
123
+
124
+ validate_api_required!
125
+
126
+ @headers["apiKey"] = @api_key
127
+ @headers["appVersionId"] = @app_version_id
128
+ @headers["beaconVersionId"] = @beacon_version_id
129
+ @headers["beaconMessageType"] = @beacon_message_type
130
+
131
+ if !@beacon_instance_id.nil?
132
+ @headers["beaconInstanceId"] = @beacon_instance_id.to_s
133
+ end
134
+
135
+ if !@data_timestamp.nil?
136
+ @headers["dataTimestamp"] = @data_timestamp.to_s
137
+ end
138
+
139
+ validate_format!
140
+
141
+ # Run named Timer as daemon thread
142
+ @timer = java.util.Timer.new("HTTP Output #{self.params['id']}", true)
143
+ end # def register
144
+
145
+ def multi_receive(events)
146
+ return if events.empty?
147
+ send_events(events)
148
+ end
149
+
150
+ class RetryTimerTask < java.util.TimerTask
151
+ def initialize(pending, event, attempt)
152
+ @pending = pending
153
+ @event = event
154
+ @attempt = attempt
155
+ super()
156
+ end
157
+
158
+ def run
159
+ @pending << [@event, @attempt]
160
+ end
161
+ end
162
+
163
+ def log_retryable_response(response)
164
+ if (response.code == 429)
165
+ @logger.debug? && @logger.debug("Encountered a 429 response, will retry. This is not serious, just flow control via HTTP")
166
+ else
167
+ @logger.warn("Encountered a retryable HTTP request in HTTP output, will retry", :code => response.code, :body => response.body)
168
+ end
169
+ end
170
+
171
+ def log_error_response(response, url, event)
172
+ log_failure(
173
+ "Encountered non-2xx HTTP code #{response.code}",
174
+ :response_code => response.code,
175
+ :url => url,
176
+ :event => event
177
+ )
178
+ end
179
+
180
+ def send_events(events)
181
+ successes = java.util.concurrent.atomic.AtomicInteger.new(0)
182
+ failures = java.util.concurrent.atomic.AtomicInteger.new(0)
183
+ retries = java.util.concurrent.atomic.AtomicInteger.new(0)
184
+ event_count = @is_batch ? 1 : events.size
185
+
186
+ pending = Queue.new
187
+ if @is_batch
188
+ pending << [events, 0]
189
+ else
190
+ events.each {|e| pending << [e, 0]}
191
+ end
192
+
193
+ while popped = pending.pop
194
+ break if popped == :done
195
+
196
+ event, attempt = popped
197
+
198
+ action, event, attempt = send_event(event, attempt)
199
+ begin
200
+ action = :failure if action == :retry && !@retry_failed
201
+
202
+ case action
203
+ when :success
204
+ successes.incrementAndGet
205
+ when :retry
206
+ retries.incrementAndGet
207
+
208
+ next_attempt = attempt+1
209
+ sleep_for = sleep_for_attempt(next_attempt)
210
+ @logger.info("Retrying http request, will sleep for #{sleep_for} seconds")
211
+ timer_task = RetryTimerTask.new(pending, event, next_attempt)
212
+ @timer.schedule(timer_task, sleep_for*1000)
213
+ when :failure
214
+ failures.incrementAndGet
215
+ else
216
+ raise "Unknown action #{action}"
217
+ end
218
+
219
+ if action == :success || action == :failure
220
+ if successes.get+failures.get == event_count
221
+ pending << :done
222
+ end
223
+ end
224
+ rescue => e
225
+ # This should never happen unless there's a flat out bug in the code
226
+ @logger.error("Error sending HTTP Request",
227
+ :class => e.class.name,
228
+ :message => e.message,
229
+ :backtrace => e.backtrace)
230
+ failures.incrementAndGet
231
+ raise e
232
+ end
233
+ end
234
+ rescue => e
235
+ @logger.error("Error in http output loop",
236
+ :class => e.class.name,
237
+ :message => e.message,
238
+ :backtrace => e.backtrace)
239
+ raise e
240
+ end
241
+
242
+ def sleep_for_attempt(attempt)
243
+ sleep_for = attempt**2
244
+ sleep_for = sleep_for <= 60 ? sleep_for : 60
245
+ (sleep_for/2) + (rand(0..sleep_for)/2)
246
+ end
247
+
248
+ def send_event(event, attempt)
249
+ body = event_body(event)
250
+
251
+ # Send the request
252
+ url = @is_batch ? @@url : event.sprintf(@@url)
253
+ headers = @is_batch ? @headers : event_headers(event)
254
+
255
+ # Compress the body and add appropriate header
256
+ if @http_compression == true
257
+ headers["Content-Encoding"] = "gzip"
258
+ body = gzip(body)
259
+ end
260
+
261
+ # Create an async request
262
+ response = client.send(@@http_method, url, :body => body, :headers => headers).call
263
+
264
+ if !response_success?(response)
265
+ if retryable_response?(response)
266
+ log_retryable_response(response)
267
+ return :retry, event, attempt
268
+ else
269
+ log_error_response(response, url, event)
270
+ return :failure, event, attempt
271
+ end
272
+ else
273
+ return :success, event, attempt
274
+ end
275
+
276
+ rescue => exception
277
+ will_retry = retryable_exception?(exception)
278
+ log_failure("Could not fetch URL",
279
+ :url => url,
280
+ :method => @@http_method,
281
+ :body => body,
282
+ :headers => headers,
283
+ :message => exception.message,
284
+ :class => exception.class.name,
285
+ :backtrace => exception.backtrace,
286
+ :will_retry => will_retry
287
+ )
288
+
289
+ if will_retry
290
+ return :retry, event, attempt
291
+ else
292
+ return :failure, event, attempt
293
+ end
294
+ end
295
+
296
+ def close
297
+ @timer.cancel
298
+ client.close
299
+ end
300
+
301
+ private
302
+
303
+ def response_success?(response)
304
+ code = response.code
305
+ return true if @ignorable_codes && @ignorable_codes.include?(code)
306
+ return code >= 200 && code <= 299
307
+ end
308
+
309
+ def retryable_response?(response)
310
+ @retryable_codes && @retryable_codes.include?(response.code)
311
+ end
312
+
313
+ def retryable_exception?(exception)
314
+ RETRYABLE_MANTICORE_EXCEPTIONS.any? {|me| exception.is_a?(me) }
315
+ end
316
+
317
+ # This is split into a separate method mostly to help testing
318
+ def log_failure(message, opts)
319
+ @logger.error("[HTTP Output Failure] #{message}", opts)
320
+ end
321
+
322
+ # Format the HTTP body
323
+ def event_body(event)
324
+ # TODO: Create an HTTP post data codec, use that here
325
+ if @format == "json"
326
+ LogStash::Json.dump(map_event(event))
327
+ elsif @format == "message"
328
+ event.sprintf(@message)
329
+ else
330
+ encode(map_event(event))
331
+ end
332
+ end
333
+
334
+ # gzip data
335
+ def gzip(data)
336
+ gz = StringIO.new
337
+ gz.set_encoding("BINARY")
338
+ z = Zlib::GzipWriter.new(gz)
339
+ z.write(data)
340
+ z.close
341
+ gz.string
342
+ end
343
+
344
+ def convert_mapping(mapping, event)
345
+ if mapping.is_a?(Hash)
346
+ mapping.reduce({}) do |acc, kv|
347
+ k, v = kv
348
+ acc[k] = convert_mapping(v, event)
349
+ acc
350
+ end
351
+ elsif mapping.is_a?(Array)
352
+ mapping.map { |elem| convert_mapping(elem, event) }
353
+ else
354
+ event.sprintf(mapping)
355
+ end
356
+ end
357
+
358
+ def map_event(event)
359
+ if @mapping
360
+ convert_mapping(@mapping, event)
361
+ else
362
+ event.to_hash
363
+ end
364
+ end
365
+
366
+ def event_headers(event)
367
+ custom_headers(event) || {}
368
+ end
369
+
370
+ def custom_headers(event)
371
+ return nil unless @headers
372
+
373
+ @headers.reduce({}) do |acc,kv|
374
+ k,v = kv
375
+ acc[k] = event.sprintf(v)
376
+ acc
377
+ end
378
+ end
379
+
380
+ #TODO Extract this to a codec
381
+ def encode(hash)
382
+ return hash.collect do |key, value|
383
+ CGI.escape(key) + "=" + CGI.escape(value.to_s)
384
+ end.join("&")
385
+ end
386
+
387
+
388
+ def validate_format!
389
+ if @format == "message"
390
+ if @message.nil?
391
+ raise "message must be set if message format is used"
392
+ end
393
+
394
+ unless @mapping.nil?
395
+ @logger.warn "mapping is not supported and will be ignored if message format is used"
396
+ end
397
+ end
398
+ end
399
+
400
+ def validate_api_required!
401
+ if @api_key.nil?
402
+ raise "api_key must be set"
403
+ end
404
+
405
+ if @app_version_id.nil?
406
+ raise "app_version_id must be set"
407
+ end
408
+
409
+ if @beacon_version_id.nil?
410
+ raise "beacon_version_id must be set"
411
+ end
412
+
413
+ if @beacon_message_type.nil?
414
+ raise "beacon_message_type must be set"
415
+ end
416
+
417
+ # # unless @mapping.nil?
418
+ # # @logger.warn "mapping is not supported and will be ignored if message format is used"
419
+ # # end
420
+ end
421
+
422
+ end # class LogStash::Outputs::HarborBeacon
@@ -0,0 +1,30 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'logstash-output-harbor_beacon'
3
+ s.version = '0.3.0'
4
+ s.licenses = ['Apache-2.0']
5
+ s.summary = 'Emit output messages (beacons) to Harbor Beacon service.'
6
+ s.description = 'This gem is a Logstash output plugin to be installed on top of the Logstash'\
7
+ 'core pipeline using $LS_HOME/bin/logstash-plugin install gemname.'\
8
+ 'This gem is not a stand-alone program.'
9
+ s.homepage = 'https://docs.hrbr.io/'
10
+ s.authors = ['Harbor']
11
+ s.email = 'info@hrbr.io'
12
+ s.require_paths = ['lib']
13
+
14
+ # Files
15
+ s.files = Dir['lib/**/*','spec/**/*','vendor/**/*','*.gemspec','*.md','CONTRIBUTORS','Gemfile','LICENSE','NOTICE.TXT']
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-plugin-api", "~> 2.0"
24
+ s.add_runtime_dependency "logstash-mixin-http_client", ">= 6.0.0", "< 7.0.0"
25
+ s.add_runtime_dependency "logstash-codec-plain", "~> 3.0"
26
+
27
+ s.add_development_dependency "logstash-devutils", "~> 1.3"
28
+ # s.add_development_dependency 'sinatra', '~> 1.4'
29
+ # s.add_development_dependency 'webrick', '~> 1.4'
30
+ end
@@ -0,0 +1,35 @@
1
+ require "logstash/devutils/rspec/spec_helper"
2
+ require "logstash/outputs/harbor_beacon"
3
+ require "logstash/codecs/plain"
4
+
5
+
6
+ class LogStash::Outputs::HarborBeacon
7
+ attr_writer :agent
8
+ attr_reader :request_tokens
9
+ end
10
+
11
+ describe LogStash::Outputs::HarborBeacon do
12
+
13
+ let(:sample_event) { LogStash::Event.new({"message" => "hi"}) }
14
+
15
+ let(:api_key) { "ABC" }
16
+ let(:app_version_id) { "DEF" }
17
+ let(:beacon_version_id) { "GHI" }
18
+ let(:beacon_message_type) { "JKL" }
19
+ let(:sample_config) { {"api_key" => api_key, "app_version_id" => app_version_id, "beacon_version_id" => beacon_version_id, "beacon_message_type" => beacon_message_type } }
20
+
21
+ let(:output) { LogStash::Outputs::HarborBeacon.new(sample_config) }
22
+
23
+ before do
24
+ output.register
25
+ end
26
+
27
+ describe "receive message" do
28
+ subject { output.receive(sample_event) }
29
+
30
+ it "returns a string" do
31
+ #expect(subject).to eq("Event received")
32
+ end
33
+ end
34
+
35
+ end
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: logstash-output-harbor_beacon
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ platform: ruby
6
+ authors:
7
+ - Harbor
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-12-21 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: 6.0.0
33
+ - - "<"
34
+ - !ruby/object:Gem::Version
35
+ version: 7.0.0
36
+ name: logstash-mixin-http_client
37
+ prerelease: false
38
+ type: :runtime
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 6.0.0
44
+ - - "<"
45
+ - !ruby/object:Gem::Version
46
+ version: 7.0.0
47
+ - !ruby/object:Gem::Dependency
48
+ requirement: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - "~>"
51
+ - !ruby/object:Gem::Version
52
+ version: '3.0'
53
+ name: logstash-codec-plain
54
+ prerelease: false
55
+ type: :runtime
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '3.0'
61
+ - !ruby/object:Gem::Dependency
62
+ requirement: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - "~>"
65
+ - !ruby/object:Gem::Version
66
+ version: '1.3'
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: '1.3'
75
+ description: This gem is a Logstash output plugin to be installed on top of the Logstashcore pipeline
76
+ using $LS_HOME/bin/logstash-plugin install gemname.This gem is not a stand-alone
77
+ program.
78
+ email: info@hrbr.io
79
+ executables: []
80
+ extensions: []
81
+ extra_rdoc_files: []
82
+ files:
83
+ - CHANGELOG.md
84
+ - CONTRIBUTORS
85
+ - Gemfile
86
+ - LICENSE
87
+ - README.md
88
+ - lib/logstash/outputs/harbor_beacon.rb
89
+ - logstash-output-harbor_beacon.gemspec
90
+ - spec/outputs/harbor_beacon_spec.rb
91
+ homepage: https://docs.hrbr.io/
92
+ licenses:
93
+ - Apache-2.0
94
+ metadata:
95
+ logstash_plugin: 'true'
96
+ logstash_group: output
97
+ post_install_message:
98
+ rdoc_options: []
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ requirements: []
112
+ rubyforge_project:
113
+ rubygems_version: 2.7.6
114
+ signing_key:
115
+ specification_version: 4
116
+ summary: Emit output messages (beacons) to Harbor Beacon service.
117
+ test_files:
118
+ - spec/outputs/harbor_beacon_spec.rb