logstash-output-jut 0.3

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
+ SHA1:
3
+ metadata.gz: a9eb2e11117af72725830240a0c80dd9ac967620
4
+ data.tar.gz: e0302a18404130b4c21ced3836e7bb91c8d8c03f
5
+ SHA512:
6
+ metadata.gz: d9a292c278f0430a56e0f0f1aa10a4605a357bbe2ef2f24672095bd25213edb94a9d85144c0534fd3a3b192542d52ca218a442b5f592238e17db20602e23345f
7
+ data.tar.gz: d389e4ddb0ca8af5da51525ffbe9e680996130a685eea176dd8f629a4ee716f1c01b423bcacddcf9a7d16d86ee4dab21ad9de58ab6d3862209ad92f6f9925786
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ .DS_Store
2
+ *.gem
3
+ Gemfile.lock
4
+ Gemfile.bak
5
+ .bundle
6
+ vendor
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 this plugin along its way.
3
+
4
+ Contributors:
5
+ * Stephan Liu (stephan-x-liu)
6
+ * Jut, Inc.
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/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+ gem "logstash", :github => "elasticsearch/logstash", :branch => "1.5"
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright (c) 2015 Jut, Inc. <www.jut.io>
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/README.md ADDED
@@ -0,0 +1,80 @@
1
+ # Batch HTTP Plugin
2
+
3
+ This is a plugin for [Logstash](https://github.com/elasticsearch/logstash).
4
+
5
+ ## Documentation
6
+
7
+ This plugin sends events to a [Jut](https://jut.io) data engine.
8
+ Events are formatted to be suitable for
9
+ [direct import via http](http://docs.jut.io/user-guide/#data_ingest_http)
10
+ and are grouped into batches to reduce the total number of http
11
+ transactions required to import many records.
12
+
13
+ ## Developing
14
+
15
+ ### 1. Plugin Developement and Testing
16
+
17
+ #### Code
18
+ - To get started, you'll need JRuby with the Bundler gem installed.
19
+
20
+ - Install dependencies
21
+ ```sh
22
+ bundle install
23
+ ```
24
+
25
+ #### Test
26
+
27
+ - Update your dependencies
28
+
29
+ ```sh
30
+ bundle install
31
+ ```
32
+
33
+ - Run tests
34
+
35
+ ```sh
36
+ bundle exec rspec
37
+ ```
38
+
39
+ ### 2. Running your unpublished Plugin in Logstash
40
+
41
+ #### 2.1 Run in a local Logstash clone
42
+
43
+ ##### 2.1.1 Version 1.5 and above
44
+
45
+ - Edit Logstash `Gemfile` and add the local plugin path, for example:
46
+ ```ruby
47
+ gem "logstash-output-jut", :path => "/your/local/logstash-output-jut"
48
+ ```
49
+ - Install plugin
50
+ ```sh
51
+ bin/plugin install --no-verify
52
+ ```
53
+ - Run Logstash with your plugin
54
+ ```sh
55
+ bin/logstash -e 'input { stdin {} } output { jut { url => URL }}'
56
+ ```
57
+ At this point any modifications to the plugin code will be applied to this local Logstash setup. After modifying the plugin, simply rerun Logstash.
58
+
59
+ ##### 2.1.1 Version 1.4 and below
60
+
61
+ - Copy the files jut.rb and HTTPBatcher.rb into /path/to/logstash-1.4.2/lib/logstash/outputs/
62
+
63
+ - Run Logstash with your plugin
64
+ ```sh
65
+ bin/logstash -e 'input { stdin {} } output { jut { url => URL }}'
66
+ ```
67
+
68
+ #### 2.2 Run in an installed Logstash
69
+
70
+ 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:
71
+
72
+ - Build your plugin gem
73
+ ```sh
74
+ gem build logstash-output-jut.gemspec
75
+ ```
76
+ - Install the plugin from the Logstash home
77
+ ```sh
78
+ bin/plugin install /your/local/plugin/logstash-output-jut.gem
79
+ ```
80
+ - Start Logstash and proceed to test the plugin
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "logstash/devutils/rake"
@@ -0,0 +1,134 @@
1
+ # encoding: utf-8
2
+ require "logstash/namespace"
3
+ require "json"
4
+ require "thread"
5
+ require "work_queue"
6
+ require "net/https"
7
+ require "openssl"
8
+ require "uri"
9
+
10
+ class LogStash::Outputs::HTTPBatcher
11
+ def initialize(url, idle_flush, logger, headers, limit, nthreads, verbose)
12
+ @url = URI.parse(url)
13
+ @idle_flush = idle_flush
14
+ @logger = logger
15
+ @headers = headers
16
+ @limit = limit
17
+ @verbose = verbose
18
+
19
+ @content_type = "application/json"
20
+ @stopped = false
21
+
22
+ @queue_mutex = Mutex.new
23
+ @queue = []
24
+
25
+ @flush_time = nil
26
+ @flush_thread = create_flush_thread()
27
+ @work_queue = WorkQueue.new nthreads, nil
28
+ end # def initialize
29
+
30
+ def stop
31
+ if @verbose
32
+ puts "stopping batcher (have #{@queue.size()} queued message)"
33
+ end
34
+
35
+ @stopped = true
36
+ while @queue.size() > 0 do
37
+ enqueue_batch
38
+ end
39
+
40
+ @flush_thread.join
41
+ @work_queue.join
42
+ end
43
+
44
+ def receive(event)
45
+ size = 0
46
+ @queue_mutex.synchronize do
47
+ @queue << event
48
+ size = @queue.size
49
+ end
50
+
51
+ if size >= @limit
52
+ enqueue_batch
53
+ end
54
+
55
+ @flush_time = Time.now + @idle_flush
56
+ end # def receive
57
+
58
+ def create_flush_thread
59
+ return Thread.new do
60
+ while !@stopped do
61
+ now = Time.now
62
+ if @flush_time != nil && @flush_time <= now
63
+ enqueue_batch
64
+ @flush_time = nil
65
+ end
66
+
67
+ sleep(@flush_time == nil ? @idle_flush : @flush_time - now)
68
+ end
69
+ end
70
+ end
71
+
72
+ def enqueue_batch
73
+ tosend = []
74
+ @queue_mutex.synchronize do
75
+ tosend = @queue.shift(@limit)
76
+ end
77
+
78
+ if tosend.size > 0
79
+ @work_queue.enqueue_b do
80
+ send_batch tosend
81
+ end
82
+ end
83
+ end
84
+
85
+ def send_batch(tosend)
86
+ connection = Thread.current["connection"]
87
+ if connection == nil
88
+ if @verbose
89
+ puts "creating new https connection"
90
+ end
91
+
92
+ connection = Net::HTTP.new(@url.host, @url.port)
93
+ connection.use_ssl = true
94
+ connection.verify_mode = OpenSSL::SSL::VERIFY_PEER
95
+ Thread.current["connection"] = connection
96
+ end
97
+
98
+ beginning = Time.now
99
+ request = Net::HTTP::Post.new(@url.request_uri)
100
+
101
+ request["Content-Type"] = @content_type
102
+ if @headers
103
+ @headers.each do |k,v|
104
+ request.headers[k] = v
105
+ end
106
+ end
107
+
108
+ if @verbose
109
+ puts "posting #{tosend.size} records"
110
+ end
111
+
112
+ request.body = tosend.to_json
113
+ response = connection.request request
114
+
115
+ status = response.code
116
+ rbody = response.read_body
117
+
118
+ if status != "200"
119
+ raise "POST failed with status #{status} (#{rbody})"
120
+ end
121
+
122
+ if @verbose
123
+ time = Time.now - beginning
124
+ puts "POST response in #{time.to_s} #{status} #{rbody}"
125
+ end
126
+
127
+ rescue Exception => e
128
+ if @verbose
129
+ @logger.warn("Unhandled exception", :request => request, :exception => e, :stacktrace => e.backtrace)
130
+ else
131
+ @logger.warn("Unhandled exception", :host => request["host"], :exception => e, :stacktrace => e.backtrace)
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,74 @@
1
+ # encoding: utf-8
2
+ require "logstash/outputs/base"
3
+ require "logstash/namespace"
4
+ require "logstash/outputs/HTTPBatcher"
5
+
6
+ class LogStash::Outputs::Jut < LogStash::Outputs::Base
7
+ # This output lets you batch JSON objects and POSTs them to
8
+ # generic HTTP endpoints. This supports only sending JSON
9
+ # and sends a request every X seconds.
10
+ #
11
+ # Additionally, you are given the option to customize
12
+ # the headers.
13
+
14
+ config_name "jut"
15
+ milestone 1
16
+
17
+ # URL to use
18
+ config :url, :validate => :string, :required => :true
19
+
20
+ # Minimum interval at which requests are made per thread
21
+ config :interval, :validate => :number, :default => 1
22
+
23
+ # Maximum number of events per request
24
+ config :limit, :validate => :number, :default => 200
25
+
26
+ # Number of concurrent threads making requests
27
+ config :threads, :validate => :number, :default => 5
28
+
29
+ # Headers, if necessary, in the form of a hash
30
+ config :headers, :validate => :hash
31
+
32
+ # Verbose error messages
33
+ config :verbose, :validate => :boolean, :default => false
34
+
35
+ public
36
+ def register
37
+ @batcher = LogStash::Outputs::HTTPBatcher.new(@url, @interval, @logger, @headers, @limit, @threads, @verbose)
38
+ end # def register
39
+
40
+ public
41
+ def receive(event)
42
+ return unless output?(event)
43
+
44
+ # logstash wants to create "@timestamp", but jut wants "time"
45
+ # you could of course do this with a mutate filter but lets make
46
+ # the common case easy...
47
+ if event.include?('@timestamp')
48
+ event['time'] = event.remove('@timestamp')
49
+ end
50
+
51
+ if event.include?('@version')
52
+ event.remove('@version')
53
+ end
54
+
55
+ evt = event.to_hash
56
+ @batcher.receive(evt)
57
+
58
+ rescue Exception => e
59
+ @logger.warn("Unhandled exception", :exception => e, :stacktrace => e.backtrace)
60
+ end
61
+
62
+ public
63
+ def teardown
64
+ if @verbose
65
+ puts "tearing down jut output plugin"
66
+ end
67
+
68
+ @batcher.stop
69
+
70
+ if @verbose
71
+ puts "finished"
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,27 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'logstash-output-jut'
3
+ s.version = "0.3"
4
+ s.licenses = ["Apache License (2.0)"]
5
+ s.summary = "sends records to a Jut data engine"
6
+ s.description = "logstash output plugin that sends records to a Jut data engine"
7
+ s.authors = ["Jut, Inc."]
8
+ s.email = "josa@jut.io"
9
+ s.homepage = "https://github.com/jut-io/logstash-output-jut"
10
+ s.require_paths = ["lib"]
11
+
12
+ # Files
13
+ s.files = `git ls-files`.split($\)
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
+ # This metadata attribute blew up for me on Ubuntu 14.04 with the latest rubgems installed
19
+ s.metadata = { "logstash_plugin" => "true", "logstash_group" => "output" }
20
+
21
+ # Gem dependencies
22
+ s.add_runtime_dependency "logstash-core", ">= 1.4.0", "< 2.0.0"
23
+ s.add_runtime_dependency "logstash-codec-plain"
24
+ s.add_runtime_dependency "work_queue"
25
+
26
+ s.add_development_dependency "logstash-devutils"
27
+ end
@@ -0,0 +1,29 @@
1
+ require "spec_helper"
2
+ require "logstash/outputs/jut"
3
+ require "logstash/codecs/plain"
4
+ require "logstash/event"
5
+
6
+ describe LogStash::Outputs::Jut do
7
+ let(:jut_config) {
8
+ {
9
+ 'url' => 'http://localhost:9000/'
10
+ }
11
+ }
12
+ let(:sample_event) { LogStash::Event.new({'message' => 'hello', '@timestamp'=>LogStash::Timestamp.now}) }
13
+
14
+ context 'when intializing' do
15
+ it 'should register' do
16
+ output = LogStash::Outputs::Jut.new(jut_config)
17
+ expect {output.register}.to_not raise_error
18
+ end
19
+
20
+ it 'should populate jut config with default values' do
21
+ cfg = LogStash::Outputs::Jut.new(jut_config)
22
+ insist {cfg.url} == 'http://localhost:9000/'
23
+ insist {cfg.interval} == 1
24
+ insist {cfg.limit} == 200
25
+ insist {cfg.threads} == 5
26
+ insist {cfg.verbose} == false
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,3 @@
1
+ require "logstash/devutils/rspec/spec_helper"
2
+ require "webmock/rspec"
3
+ WebMock.disable_net_connect!(allow_localhost: true)
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: logstash-output-jut
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.3'
5
+ platform: ruby
6
+ authors:
7
+ - Jut, Inc.
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-09-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: logstash-core
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: 1.4.0
20
+ - - <
21
+ - !ruby/object:Gem::Version
22
+ version: 2.0.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 1.4.0
30
+ - - <
31
+ - !ruby/object:Gem::Version
32
+ version: 2.0.0
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: work_queue
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - '>='
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: logstash-devutils
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - '>='
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - '>='
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ description: logstash output plugin that sends records to a Jut data engine
76
+ email: josa@jut.io
77
+ executables: []
78
+ extensions: []
79
+ extra_rdoc_files: []
80
+ files:
81
+ - .gitignore
82
+ - CONTRIBUTORS
83
+ - Gemfile
84
+ - LICENSE
85
+ - README.md
86
+ - Rakefile
87
+ - lib/logstash/outputs/HTTPBatcher.rb
88
+ - lib/logstash/outputs/jut.rb
89
+ - logstash-output-jut.gemspec
90
+ - spec/outputs/jut_spec.rb
91
+ - spec/spec_helper.rb
92
+ homepage: https://github.com/jut-io/logstash-output-jut
93
+ licenses:
94
+ - Apache License (2.0)
95
+ metadata:
96
+ logstash_plugin: 'true'
97
+ logstash_group: output
98
+ post_install_message:
99
+ rdoc_options: []
100
+ require_paths:
101
+ - lib
102
+ required_ruby_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - '>='
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ required_rubygems_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - '>='
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ requirements: []
113
+ rubyforge_project:
114
+ rubygems_version: 2.0.14
115
+ signing_key:
116
+ specification_version: 4
117
+ summary: sends records to a Jut data engine
118
+ test_files:
119
+ - spec/outputs/jut_spec.rb
120
+ - spec/spec_helper.rb