diff --git a/README.md b/README.md index a5c77f5..a1d996c 100644 --- a/README.md +++ b/README.md @@ -257,6 +257,24 @@ LogStashLogger.new \ verify_hostname: false ``` +## HTTP +Supports rudimentary writes (buffered, non-persistent connections) to the[ Logstash HTTP Input](https://www.elastic.co/guide/en/logstash/current/plugins-inputs-http.html): +```ruby +input { + http { + port => 8080 + } +} +``` + +```ruby +LogStashLogger.new \ + type: :http, + url: 'http://localhost:8080' +``` + +Note the parameter is `url` and not `uri`. Relies on [Net:HTTP](https://ruby-doc.org/stdlib-2.7.1/libdoc/net/http/rdoc/Net/HTTP.html#class-Net::HTTP-label-HTTPS) to auto-detect SSL usage from the scheme. + ## Custom Log Fields `LogStashLogger` by default will log a JSON object with the format below. diff --git a/lib/logstash-logger/device.rb b/lib/logstash-logger/device.rb index bb3facb..5fdc9fd 100644 --- a/lib/logstash-logger/device.rb +++ b/lib/logstash-logger/device.rb @@ -20,6 +20,7 @@ module Device autoload :Stderr, 'logstash-logger/device/stderr' autoload :Balancer, 'logstash-logger/device/balancer' autoload :MultiDelegator, 'logstash-logger/device/multi_delegator' + autoload :HTTP, 'logstash-logger/device/http' def self.new(opts) opts = opts.dup @@ -60,6 +61,7 @@ def self.device_klass_for(type) when :stderr then Stderr when :multi_delegator then MultiDelegator when :balancer then Balancer + when :http then HTTP else fail ArgumentError, 'Invalid device type' end end diff --git a/lib/logstash-logger/device/http.rb b/lib/logstash-logger/device/http.rb new file mode 100644 index 0000000..2d58e0b --- /dev/null +++ b/lib/logstash-logger/device/http.rb @@ -0,0 +1,33 @@ +require 'uri' +require 'net/http' + +module LogStashLogger + module Device + + # Rudimentary write to Logstash HTTP Input relying on buffering + # rather than persistent HTTP connections for efficiency. + class HTTP < Connectable + + def initialize(opts) + super + @url = URI(opts[:url]) + end + + def connect + # no-op + end + + def write_one(message) + write_batch([message]) + end + + def write_batch(messages, group = nil) + # Logstash HTTP input expects JSON array instead of lines of JSON + body = "[#{messages.join(',')}]" + resp = Net::HTTP.post @url, body, {"Content-Type" => "application/json"} + raise resp.message if Net::HTTPError === resp + end + + end + end +end diff --git a/spec/device/http_spec.rb b/spec/device/http_spec.rb new file mode 100644 index 0000000..d56cfbd --- /dev/null +++ b/spec/device/http_spec.rb @@ -0,0 +1,11 @@ +require 'logstash-logger' + +describe LogStashLogger::Device::HTTP do + include_context 'device' + + it "Post event to HTTP" do + expect(Net::HTTP).to receive(:post) + http_device.write('test') + end + +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 966400e..f84a0e1 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -53,6 +53,7 @@ def connection_type let(:tcp_device) { LogStashLogger::Device.new(type: :tcp, port: port, sync: true) } let(:ssl_tcp_device) { LogStashLogger::Device.new(type: :tcp, port: port, ssl_enable: true, sync: true) } let(:unix_device) { LogStashLogger::Device.new(type: :unix, path: '/tmp/logstash', sync: true) } + let(:http_device) { LogStashLogger::Device.new(type: :http, url: 'http://localhost', sync: true) } let(:file) { Tempfile.new('test') } let(:file_device) { LogStashLogger::Device.new(type: :file, path: file.path)}