logstash-output-unomaly 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile +11 -0
- data/README.md +40 -0
- data/lib/logstash/outputs/unomaly.rb +333 -0
- data/logstash-output-unomaly.gemspec +28 -0
- data/spec/outputs/unomaly_spec.rb +46 -0
- metadata +122 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 4a098c44ba976c41ad152a148d265a6a7ece4216307104156ae1ff9f578589cd
|
4
|
+
data.tar.gz: b1dc31ddc497b25ae5895aa5407117047f695cc03d1ccc99e4f69f605e3cc9e0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1b9e317eab598594b691766d46199d12dfe3291baa7e49863762cae50e7a9679693f1d35ed80ce69c4d033908171b9026a19e910c78c818b979ca0d5a7b23cf0
|
7
|
+
data.tar.gz: cecec9cc21f9dc4c23663010be493597baeb082c73eecec5e3fcd122bdfac1f679b303e275e0a6659d6c669b3fe5a25a829c7be9517a45e4046b04e32e2b9fbb
|
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
gemspec
|
4
|
+
|
5
|
+
logstash_path = ENV["LOGSTASH_PATH"] || "../../logstash"
|
6
|
+
use_logstash_source = ENV["LOGSTASH_SOURCE"] && ENV["LOGSTASH_SOURCE"].to_s == "1"
|
7
|
+
|
8
|
+
if Dir.exist?(logstash_path) && use_logstash_source
|
9
|
+
gem 'logstash-core', :path => "#{logstash_path}/logstash-core"
|
10
|
+
gem 'logstash-core-plugin-api', :path => "#{logstash_path}/logstash-core-plugin-api"
|
11
|
+
end
|
data/README.md
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# Logstash output plugin for Unomaly
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/unomaly/logstash-output-unomaly.svg?branch=master)](https://travis-ci.org/unomaly/logstash-output-unomaly)
|
4
|
+
|
5
|
+
This plugin sends Logstash events to the [Unomaly](https://www.unomaly.com) ingestion API (min version Unomaly 2.27). The minimal configuration looks like this:
|
6
|
+
|
7
|
+
|
8
|
+
```
|
9
|
+
output {
|
10
|
+
unomaly {
|
11
|
+
host => "https://your-unomaly-instance:443"
|
12
|
+
}
|
13
|
+
}
|
14
|
+
```
|
15
|
+
|
16
|
+
# Important options
|
17
|
+
|
18
|
+
|
19
|
+
| Option | Description | Default |
|
20
|
+
|----------------------------|----------------------------------------------------------------------------------|------------|
|
21
|
+
| host | Unomaly instance address. Must define full path such as "https://my-instance:443"| No default |
|
22
|
+
| message_key | The key in the Logstash event that Unomaly should use for anomaly detection. | "message" |
|
23
|
+
| source_key | The event key defining the Unomaly system. | "host" |
|
24
|
+
| ssl_certificate_validation | Enable or disable SSL certificate validation | "strict" |
|
25
|
+
|
26
|
+
See the [source code](lib/logstash/outputs/unomaly.rb) for the full list of options
|
27
|
+
|
28
|
+
|
29
|
+
## Known issues
|
30
|
+
- Installation of the plugin fails on Logstash 6.2.1.
|
31
|
+
|
32
|
+
|
33
|
+
## Contributing
|
34
|
+
|
35
|
+
Bug reports and pull requests are welcome. This project is intended to
|
36
|
+
be a safe, welcoming space for collaboration.
|
37
|
+
|
38
|
+
## Development
|
39
|
+
|
40
|
+
We use docker to build the plugin. You can build it by running `docker-compose run jruby gem build logstash-output-unomaly.gemspec `
|
@@ -0,0 +1,333 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "logstash/outputs/base"
|
3
|
+
require "logstash/namespace"
|
4
|
+
require "logstash/json"
|
5
|
+
require 'uri'
|
6
|
+
require 'json'
|
7
|
+
require 'manticore'
|
8
|
+
|
9
|
+
# An example output that does nothing.
|
10
|
+
class LogStash::Outputs::Unomaly < LogStash::Outputs::Base
|
11
|
+
class InvalidHTTPConfigError < StandardError; end
|
12
|
+
|
13
|
+
concurrency :shared
|
14
|
+
config_name "unomaly"
|
15
|
+
|
16
|
+
# Event batch size to send to Unomaly. Increasing the batch size can increase throughput by reducing HTTP overhead
|
17
|
+
config :batch_size, :validate => :number, :default => 50
|
18
|
+
|
19
|
+
# Unomaly host to send the logs to
|
20
|
+
config :host, :validate => :string, :required => true
|
21
|
+
|
22
|
+
# Key that will be used by Unomaly as the log message
|
23
|
+
config :message_key, :validate => :string, :default => "message"
|
24
|
+
|
25
|
+
# Key that will be used by Unomaly as the system key
|
26
|
+
config :source_key, :validate => :string, :default => "host"
|
27
|
+
|
28
|
+
# Unomaly api path to push events
|
29
|
+
config :api_path, :validate => :string, :default => "/v1/batch"
|
30
|
+
|
31
|
+
# Keep logstash timestamp
|
32
|
+
config :keep_timestamp, :validate => :boolean, :default => true
|
33
|
+
|
34
|
+
# Display debug logs
|
35
|
+
config :debug, :validate => :boolean, :default => false
|
36
|
+
|
37
|
+
# Timeout (in seconds) for the entire request
|
38
|
+
config :request_timeout, :validate => :number, :default => 60
|
39
|
+
|
40
|
+
# Timeout (in seconds) to wait for data on the socket. Default is `10s`
|
41
|
+
config :socket_timeout, :validate => :number, :default => 10
|
42
|
+
|
43
|
+
# Timeout (in seconds) to wait for a connection to be established. Default is `10s`
|
44
|
+
config :connect_timeout, :validate => :number, :default => 10
|
45
|
+
|
46
|
+
# Should redirects be followed? Defaults to `true`
|
47
|
+
config :follow_redirects, :validate => :boolean, :default => true
|
48
|
+
|
49
|
+
# Max number of concurrent connections. Defaults to `50`
|
50
|
+
config :pool_max, :validate => :number, :default => 50
|
51
|
+
|
52
|
+
# Max number of concurrent connections to a single host. Defaults to `25`
|
53
|
+
config :pool_max_per_route, :validate => :number, :default => 25
|
54
|
+
|
55
|
+
# Turn this on to enable HTTP keepalive support. We highly recommend setting `automatic_retries` to at least
|
56
|
+
# one with this to fix interactions with broken keepalive implementations.
|
57
|
+
config :keepalive, :validate => :boolean, :default => true
|
58
|
+
|
59
|
+
# How many times should the client retry a failing URL. We highly recommend NOT setting this value
|
60
|
+
# to zero if keepalive is enabled. Some servers incorrectly end keepalives early requiring a retry!
|
61
|
+
# Note: if `retry_non_idempotent` is set only GET, HEAD, PUT, DELETE, OPTIONS, and TRACE requests will be retried.
|
62
|
+
config :automatic_retries, :validate => :number, :default => 1
|
63
|
+
|
64
|
+
# If `automatic_retries` is enabled this will cause non-idempotent HTTP verbs (such as POST) to be retried.
|
65
|
+
config :retry_non_idempotent, :validate => :boolean, :default => false
|
66
|
+
|
67
|
+
# How long to wait before checking if the connection is stale before executing a request on a connection using keepalive.
|
68
|
+
# # You may want to set this lower, possibly to 0 if you get connection errors regularly
|
69
|
+
# Quoting the Apache commons docs (this client is based Apache Commmons):
|
70
|
+
# 'Defines period of inactivity in milliseconds after which persistent connections must be re-validated prior to being leased to the consumer. Non-positive value passed to this method disables connection validation. This check helps detect connections that have become stale (half-closed) while kept inactive in the pool.'
|
71
|
+
# See https://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/impl/conn/PoolingHttpClientConnectionManager.html#setValidateAfterInactivity(int)[these docs for more info]
|
72
|
+
config :validate_after_inactivity, :validate => :number, :default => 200
|
73
|
+
|
74
|
+
# refer to https://github.com/cheald/manticore/blob/6764e2d3fb67a1ef244cb4610d5b74ba1dd6694c/lib/manticore/client.rb#L161
|
75
|
+
# instead of using symbol, use string "disable" or "false", "browser", "strict"
|
76
|
+
config :ssl_certificate_validation, :validate => :string, :default => "strict"
|
77
|
+
|
78
|
+
# If you need to use a custom X.509 CA (.pem certs) specify the path to that here
|
79
|
+
config :cacert, :validate => :path
|
80
|
+
|
81
|
+
# If you'd like to use a client certificate (note, most people don't want this) set the path to the x509 cert here
|
82
|
+
config :client_cert, :validate => :path
|
83
|
+
# If you're using a client certificate specify the path to the encryption key here
|
84
|
+
config :client_key, :validate => :path
|
85
|
+
|
86
|
+
# If you need to use a custom keystore (`.jks`) specify that here. This does not work with .pem keys!
|
87
|
+
config :keystore, :validate => :path
|
88
|
+
|
89
|
+
# Specify the keystore password here.
|
90
|
+
# Note, most .jks files created with keytool require a password!
|
91
|
+
config :keystore_password, :validate => :password
|
92
|
+
|
93
|
+
# Specify the keystore type here. One of `JKS` or `PKCS12`. Default is `JKS`
|
94
|
+
config :keystore_type, :validate => :string, :default => "JKS"
|
95
|
+
|
96
|
+
# If you need to use a custom truststore (`.jks`) specify that here. This does not work with .pem certs!
|
97
|
+
config :truststore, :validate => :path
|
98
|
+
|
99
|
+
# Specify the truststore password here.
|
100
|
+
# Note, most .jks files created with keytool require a password!
|
101
|
+
config :truststore_password, :validate => :password
|
102
|
+
|
103
|
+
# Specify the truststore type here. One of `JKS` or `PKCS12`. Default is `JKS`
|
104
|
+
config :truststore_type, :validate => :string, :default => "JKS"
|
105
|
+
|
106
|
+
# Enable cookie support. With this enabled the client will persist cookies
|
107
|
+
# across requests as a normal web browser would. Enabled by default
|
108
|
+
config :cookies, :validate => :boolean, :default => true
|
109
|
+
|
110
|
+
# If you'd like to use an HTTP proxy . This supports multiple configuration syntaxes:
|
111
|
+
#
|
112
|
+
# 1. Proxy host in form: `http://proxy.org:1234`
|
113
|
+
# 2. Proxy host in form: `{host => "proxy.org", port => 80, scheme => 'http', user => 'username@host', password => 'password'}`
|
114
|
+
# 3. Proxy host in form: `{url => 'http://proxy.org:1234', user => 'username@host', password => 'password'}`
|
115
|
+
config :proxy
|
116
|
+
|
117
|
+
# Username to use for HTTP auth.
|
118
|
+
config :user, :validate => :string
|
119
|
+
|
120
|
+
# Password to use for HTTP auth
|
121
|
+
config :password, :validate => :password
|
122
|
+
|
123
|
+
public
|
124
|
+
def register
|
125
|
+
@total = 0
|
126
|
+
@total_failed = 0
|
127
|
+
logger.info("Initialized Unomaly output plugin with configuration",
|
128
|
+
:host => @host,
|
129
|
+
:accept_self_signed_cert => @accept_self_signed_cert)
|
130
|
+
|
131
|
+
end # def register
|
132
|
+
|
133
|
+
def client_config
|
134
|
+
c = {
|
135
|
+
connect_timeout: @connect_timeout,
|
136
|
+
socket_timeout: @socket_timeout,
|
137
|
+
request_timeout: @request_timeout,
|
138
|
+
follow_redirects: @follow_redirects,
|
139
|
+
automatic_retries: @automatic_retries,
|
140
|
+
retry_non_idempotent: @retry_non_idempotent,
|
141
|
+
check_connection_timeout: @validate_after_inactivity,
|
142
|
+
pool_max: @pool_max,
|
143
|
+
pool_max_per_route: @pool_max_per_route,
|
144
|
+
cookies: @cookies,
|
145
|
+
keepalive: @keepalive
|
146
|
+
}
|
147
|
+
|
148
|
+
if @proxy
|
149
|
+
# Symbolize keys if necessary
|
150
|
+
c[:proxy] = @proxy.is_a?(Hash) ?
|
151
|
+
@proxy.reduce({}) {|memo,(k,v)| memo[k.to_sym] = v; memo} :
|
152
|
+
@proxy
|
153
|
+
end
|
154
|
+
|
155
|
+
if @user
|
156
|
+
if !@password || !@password.value
|
157
|
+
raise ::LogStash::ConfigurationError, "User '#{@user}' specified without password!"
|
158
|
+
end
|
159
|
+
|
160
|
+
# Symbolize keys if necessary
|
161
|
+
c[:auth] = {
|
162
|
+
:user => @user,
|
163
|
+
:password => @password.value,
|
164
|
+
:eager => true
|
165
|
+
}
|
166
|
+
end
|
167
|
+
|
168
|
+
c[:ssl] = {}
|
169
|
+
if @ssl_certificate_validation == "disable" || @ssl_certificate_validation == "false"
|
170
|
+
c[:ssl][:verify] = :disable
|
171
|
+
elsif @ssl_certificate_validation == "browser"
|
172
|
+
c[:ssl][:verify] = :browser
|
173
|
+
end
|
174
|
+
|
175
|
+
if @cacert
|
176
|
+
c[:ssl][:ca_file] = @cacert
|
177
|
+
end
|
178
|
+
|
179
|
+
if @truststore
|
180
|
+
c[:ssl].merge!(
|
181
|
+
:truststore => @truststore,
|
182
|
+
:truststore_type => @truststore_type,
|
183
|
+
:truststore_password => @truststore_password.value
|
184
|
+
)
|
185
|
+
|
186
|
+
if c[:ssl][:truststore_password].nil?
|
187
|
+
raise LogStash::ConfigurationError, "Truststore declared without a password! This is not valid, please set the 'truststore_password' option"
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
if @keystore
|
192
|
+
c[:ssl].merge!(
|
193
|
+
:keystore => @keystore,
|
194
|
+
:keystore_type => @keystore_type,
|
195
|
+
:keystore_password => @keystore_password.value
|
196
|
+
)
|
197
|
+
|
198
|
+
if c[:ssl][:keystore_password].nil?
|
199
|
+
raise LogStash::ConfigurationError, "Keystore declared without a password! This is not valid, please set the 'keystore_password' option"
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
if @client_cert && @client_key
|
204
|
+
c[:ssl][:client_cert] = @client_cert
|
205
|
+
c[:ssl][:client_key] = @client_key
|
206
|
+
elsif !!@client_cert ^ !!@client_key
|
207
|
+
raise InvalidHTTPConfigError, "You must specify both client_cert and client_key for an HTTP client, or neither!"
|
208
|
+
end
|
209
|
+
|
210
|
+
c
|
211
|
+
end
|
212
|
+
|
213
|
+
private
|
214
|
+
def make_client
|
215
|
+
puts client_config
|
216
|
+
Manticore::Client.new(client_config)
|
217
|
+
end
|
218
|
+
|
219
|
+
public
|
220
|
+
def client
|
221
|
+
@client ||= make_client
|
222
|
+
end
|
223
|
+
|
224
|
+
public
|
225
|
+
def close
|
226
|
+
@client.close
|
227
|
+
end
|
228
|
+
|
229
|
+
def flatten(data, prefix)
|
230
|
+
ret = {}
|
231
|
+
if data.is_a? Hash
|
232
|
+
data.each { |key, value|
|
233
|
+
if prefix.to_s.empty?
|
234
|
+
ret.merge! flatten(value, "#{key.to_s}")
|
235
|
+
else
|
236
|
+
ret.merge! flatten(value, "#{prefix}__#{key.to_s}")
|
237
|
+
end
|
238
|
+
}
|
239
|
+
elsif data.is_a? Array
|
240
|
+
data.each_with_index {|val,index | ret.merge! flatten(val, "#{prefix}__#{index}")}
|
241
|
+
else
|
242
|
+
return {prefix => data.to_s}
|
243
|
+
end
|
244
|
+
|
245
|
+
ret
|
246
|
+
end
|
247
|
+
|
248
|
+
def send_batch(events)
|
249
|
+
url = @host + @api_path
|
250
|
+
body = events.to_json
|
251
|
+
|
252
|
+
request = client.post(url, {
|
253
|
+
:body => body,
|
254
|
+
:headers => {"Content-Type"=>"application/json"}
|
255
|
+
})
|
256
|
+
|
257
|
+
request.on_success do |response|
|
258
|
+
if response.code == 200
|
259
|
+
@logger.debug("Successfully sent ",
|
260
|
+
:response_code => response.code,
|
261
|
+
:total => @total,
|
262
|
+
:time => Time::now.utc)
|
263
|
+
else
|
264
|
+
@total_failed += 1
|
265
|
+
log_failure(
|
266
|
+
"Encountered non-200 HTTP code #{response.code}",
|
267
|
+
:response_code => response.code,
|
268
|
+
:url => url,
|
269
|
+
:response_body => response.body,
|
270
|
+
:total_failed => @total_failed)
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
request.on_failure do |exception|
|
275
|
+
@total_failed += 1
|
276
|
+
log_failure("Could not access URL",
|
277
|
+
:url => url,
|
278
|
+
:method => @http_method,
|
279
|
+
:body => body,
|
280
|
+
:message => exception.message,
|
281
|
+
:class => exception.class.name,
|
282
|
+
:backtrace => exception.backtrace,
|
283
|
+
:total_failed => @total_failed
|
284
|
+
)
|
285
|
+
end
|
286
|
+
|
287
|
+
@logger.debug("Sending Unomaly Event",
|
288
|
+
:total => @total,
|
289
|
+
:time => Time::now.utc)
|
290
|
+
request.call
|
291
|
+
|
292
|
+
rescue Exception => e
|
293
|
+
@logger.error("[Exception=] #{e.message} #{e.backtrace}")
|
294
|
+
end
|
295
|
+
|
296
|
+
|
297
|
+
public
|
298
|
+
def multi_receive(events)
|
299
|
+
if debug
|
300
|
+
puts events.to_json
|
301
|
+
end
|
302
|
+
|
303
|
+
events.each_slice(@batch_size) do |chunk|
|
304
|
+
documents = []
|
305
|
+
chunk.each do |event|
|
306
|
+
unomaly_event = {
|
307
|
+
message: event.get(@message_key),
|
308
|
+
source: event.get(@source_key),
|
309
|
+
}
|
310
|
+
|
311
|
+
metadata = event.to_hash
|
312
|
+
|
313
|
+
if @keep_timestamp
|
314
|
+
unomaly_event["timestamp"] = event.get("@timestamp")
|
315
|
+
metadata.delete("@timestamp")
|
316
|
+
end
|
317
|
+
|
318
|
+
metadata.delete(@source_key)
|
319
|
+
metadata.delete(@message_key)
|
320
|
+
|
321
|
+
unomaly_event["metadata"]=flatten(metadata,"")
|
322
|
+
|
323
|
+
|
324
|
+
documents.push(unomaly_event)
|
325
|
+
end
|
326
|
+
send_batch(documents)
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
def log_failure(message, opts)
|
331
|
+
@logger.error("[HTTP Output Failure] #{message}", opts)
|
332
|
+
end
|
333
|
+
end # class LogStash::Outputs::Unomaly
|
@@ -0,0 +1,28 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'logstash-output-unomaly'
|
3
|
+
s.version = '0.1.3'
|
4
|
+
s.licenses = ['Apache License (2.0)']
|
5
|
+
s.summary = "Logstash output plugin for Unomaly"
|
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.authors = ["Unomaly"]
|
8
|
+
s.email = "support@unomaly.com"
|
9
|
+
s.homepage = "https://unomaly.com"
|
10
|
+
s.require_paths = ["lib"]
|
11
|
+
|
12
|
+
# Files
|
13
|
+
s.files = Dir['lib/**/*','spec/**/*','vendor/**/*','*.gemspec','*.md','Gemfile']
|
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
|
+
#
|
22
|
+
|
23
|
+
s.add_runtime_dependency "logstash-core-plugin-api", ">= 1.60", "<= 2.99"
|
24
|
+
s.add_runtime_dependency "logstash-codec-plain"
|
25
|
+
s.add_runtime_dependency 'manticore', '>= 0.5.2', '< 1.0.0'
|
26
|
+
|
27
|
+
s.add_development_dependency 'logstash-devutils'
|
28
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "logstash/devutils/rspec/spec_helper"
|
3
|
+
require "logstash/outputs/unomaly"
|
4
|
+
require "logstash/event"
|
5
|
+
|
6
|
+
describe LogStash::Outputs::Unomaly do
|
7
|
+
let(:sample_event) { LogStash::Event.new("message" => "hello this is log") }
|
8
|
+
let(:client) { @unomaly.client }
|
9
|
+
|
10
|
+
before do
|
11
|
+
@unomaly = LogStash::Outputs::Unomaly.new("host" => "localhost", "batch_size" => 3)
|
12
|
+
@unomaly.register
|
13
|
+
allow(@unomaly).to receive(:client).and_return(client)
|
14
|
+
allow(client).to receive(:post).and_call_original
|
15
|
+
end
|
16
|
+
|
17
|
+
before do
|
18
|
+
allow(@unomaly).to receive(:client).and_return(client)
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
it "Forwards an event" do
|
23
|
+
expect(client).to receive(:post).once.and_call_original
|
24
|
+
@unomaly.multi_receive([sample_event])
|
25
|
+
end
|
26
|
+
|
27
|
+
it "Batches multiple events and extracts metadata" do
|
28
|
+
event1 = LogStash::Event.new("message" => "hello this is log 1", "host" => "host1")
|
29
|
+
event2 = LogStash::Event.new("message" => "hello this is log 2", "host" => "host2")
|
30
|
+
event3 = LogStash::Event.new("message" => "hello this is log 3", "host" => "host3")
|
31
|
+
expect(client).to receive(:post).once.with("localhost/v1/batch",hash_including(:body => LogStash::Json.dump(
|
32
|
+
[{"message" => "hello this is log 1", "source" => "host1", "timestamp" => event1.timestamp.to_s, "metadata" => {"@version" => "1"}},
|
33
|
+
{"message" => "hello this is log 2", "source" => "host2", "timestamp" => event2.timestamp.to_s, "metadata" => {"@version" => "1"}},
|
34
|
+
{"message" => "hello this is log 3", "source" => "host3", "timestamp" => event3.timestamp.to_s, "metadata" => {"@version" => "1"}}
|
35
|
+
]
|
36
|
+
))).and_call_original
|
37
|
+
@unomaly.multi_receive([event1, event2, event3])
|
38
|
+
end
|
39
|
+
|
40
|
+
it "Batches data of size batch_size" do
|
41
|
+
expect(client).to receive(:post).exactly(2).times.and_call_original
|
42
|
+
@unomaly.multi_receive([sample_event, sample_event, sample_event, sample_event])
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
end
|
metadata
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: logstash-output-unomaly
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Unomaly
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-06-15 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: logstash-core-plugin-api
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.60'
|
20
|
+
- - "<="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '2.99'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.60'
|
30
|
+
- - "<="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '2.99'
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: logstash-codec-plain
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
type: :runtime
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: manticore
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 0.5.2
|
54
|
+
- - "<"
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: 1.0.0
|
57
|
+
type: :runtime
|
58
|
+
prerelease: false
|
59
|
+
version_requirements: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: 0.5.2
|
64
|
+
- - "<"
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: 1.0.0
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: logstash-devutils
|
69
|
+
requirement: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
type: :development
|
75
|
+
prerelease: false
|
76
|
+
version_requirements: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '0'
|
81
|
+
description: This gem is a Logstash plugin required to be installed on top of the
|
82
|
+
Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This
|
83
|
+
gem is not a stand-alone program
|
84
|
+
email: support@unomaly.com
|
85
|
+
executables: []
|
86
|
+
extensions: []
|
87
|
+
extra_rdoc_files: []
|
88
|
+
files:
|
89
|
+
- CHANGELOG.md
|
90
|
+
- Gemfile
|
91
|
+
- README.md
|
92
|
+
- lib/logstash/outputs/unomaly.rb
|
93
|
+
- logstash-output-unomaly.gemspec
|
94
|
+
- spec/outputs/unomaly_spec.rb
|
95
|
+
homepage: https://unomaly.com
|
96
|
+
licenses:
|
97
|
+
- Apache License (2.0)
|
98
|
+
metadata:
|
99
|
+
logstash_plugin: 'true'
|
100
|
+
logstash_group: output
|
101
|
+
post_install_message:
|
102
|
+
rdoc_options: []
|
103
|
+
require_paths:
|
104
|
+
- lib
|
105
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
requirements: []
|
116
|
+
rubyforge_project:
|
117
|
+
rubygems_version: 2.7.7
|
118
|
+
signing_key:
|
119
|
+
specification_version: 4
|
120
|
+
summary: Logstash output plugin for Unomaly
|
121
|
+
test_files:
|
122
|
+
- spec/outputs/unomaly_spec.rb
|