fluent-plugin-vmware-aria-log-intelligence 2.0.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 46225b14815eaaa0bbbf25007488eb19c4ab58f7fba5adeb2256888a9bec1371
4
+ data.tar.gz: 93123202b248e40de0b0d199d875a2291731d478ca6658c3e0bfeb9b18cb6f27
5
+ SHA512:
6
+ metadata.gz: 653bb8fe0202fb5557f569fd356df5ae8e0d6a5eb5cd7afe37ba0cb35cffb98f9acc86275b448b6d760b2c215c48e3492a47a351da27e518a9da1af712a9ecdc
7
+ data.tar.gz: 9c321a8e781a8cc1fa29ae26cc96e5866bc7a8f67530366501e46f8af4c9a08701ad8fb5633211aa8b08525b030a71047f115f100ca19d80c7ae8cc902e4a9c5
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ # Copyright (c) 2013 ablagoev
2
+ # Copyright 2018 VMware, Inc.
3
+ # SPDX-License-Identifier: MIT
4
+
5
+ source "http://rubygems.org"
6
+
7
+ gemspec
8
+
9
+ group :test do
10
+ gem 'coveralls', require: false
11
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,15 @@
1
+ fluent-plugin-vmware-log-intelligence
2
+
3
+ Copyright (c) 2013 ablagoev
4
+ Copyright (c) 2018 VMware, Inc.
5
+
6
+ The MIT license (the "License") set forth below applies to all parts of the fluent-plugin-vmware-log-intelligence project. You may not use this file except in compliance with the License.
7
+
8
+ MIT License
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do
11
+ so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,113 @@
1
+ = Overview
2
+
3
+ vmware_log_intelligence is a Fluentd output plugin that buffers data and sends the data to VMware Log Intelligence.
4
+
5
+ == Requirements
6
+
7
+ fluentd version: 0.14.0 or above
8
+
9
+ Ruby version: 2.2 or above
10
+
11
+ == Build and install the plugin
12
+
13
+ `gem build fluent-plugin-vmware-log-intelligence.gemspec`
14
+
15
+ `gem install fluent-plugin-vmware-log-intelligence-1.0.0.gem`
16
+
17
+ == Usage
18
+ vmware_log_intelligence does not support Fluentd v0.12 style configurations.
19
+ Please use Fluentd v1.0 style configurations when working with the plugin.
20
+
21
+ Here is an example of the vmware_log_intelligence plugin configurations:
22
+
23
+ <match pattern>
24
+ @type vmware_log_intelligence
25
+ endpoint_url https://data.upgrade.symphony-dev.com/le-mans/v1/streams/ingestion-pipeline-stream
26
+ verify_ssl false
27
+ http_compress false
28
+ <headers>
29
+ Content-Type application/json
30
+ Authorization Bearer Your-VMware-Log-Intelligence-API-Token
31
+ structure simple
32
+ </headers>
33
+ <buffer>
34
+ @type file
35
+ path /path/to/buffer/lint.buf
36
+ chunk_limit_records 300
37
+ flush_interval 20s
38
+ delayed_commit_timeout 120s
39
+ retry_max_times 3
40
+ </buffer>
41
+ <format>
42
+ @type json
43
+ tag_key text
44
+ </format>
45
+ </match>
46
+
47
+ You can find more configuration examples in the files under the examples folder.
48
+
49
+ Besides Fluentd, you can also use the plugin with td-agent.
50
+
51
+ == Payload format
52
+ VMware Log Intelligence expects the body of the HTTP requests you send to be in JSON format. It also expects the JSON payload to have a 'text' field that contains your raw log text.
53
+
54
+ The fluentd way of turning raw log text into a JSON is to use the JSON formatter plugin (https://docs.fluentd.org/v0.12/articles/formatter_json). The plugin uses a 'tag' field to store the text of a log. One configuration option of the JSON formatter plugin is called 'tag_key'. You need to set 'tag_key' to 'text' so that the JSON formatter will generate JSON payloads that conform to what VMware Log Intelligence expects.
55
+
56
+ == Configuration
57
+
58
+ === endpoint_url
59
+ This is the URL to which you want to send the logs to. You should set it to the URL of VMware Log Intelligence:
60
+ https://data.upgrade.symphony-dev.com/le-mans/v1/streams/ingestion-pipeline-stream
61
+
62
+ === verify_ssl
63
+ You can use this setting to control whether the vmware_log_intelligence plugin should verify
64
+ the SSL certificate of the endpoint_url. You should always set this to `true` except in testing
65
+ or troubleshooting.
66
+
67
+ === http_compress
68
+ You can use this setting to compress the data while sending it to vRealize Log Insight Cloud. The default value is set 'false' here, If needed you can set it 'true'.
69
+
70
+ === <headers>
71
+ In order to send logs to VMware Log Intelligence, you need to have the `<headers>`
72
+ section in your configuration. The `<headers>` section should look like the following:
73
+
74
+ <headers>
75
+ Content-Type application/json
76
+ Authorization Bearer Your-VMware-Log-Intelligence-API-Token
77
+ structure simple
78
+ </headers>
79
+
80
+ === <buffer>
81
+ The vmware_log_intelligence plugin supports buffering. All the buffering configurations
82
+ of Fluentd are supported. Detailed information of the buffering configurations can be
83
+ found here https://docs.fluentd.org/v1.0/articles/buffer-section.
84
+
85
+ == Run tests
86
+ If you would like to contribute to this project, you might want to be able to run the tests
87
+ under the `test` folder. You can do that in a Ruby environment you have set up on your dev
88
+ machine. You can also run the tests in a Docker container. To run the tests in a Docker container,
89
+ you can follow these steps:
90
+
91
+ Go to the root folder of this project and build the Docker image like this:
92
+ `docker build -t fluent-plugin-dev .`
93
+
94
+ Run the container like this:
95
+ `docker run --rm -i -t --name fluent-plugin-dev -v $(pwd):/app fluent-plugin-dev /bin/bash`
96
+
97
+ Once inside the container, you can run the following to make sure that all dependecies are installed:
98
+ `bundle install`
99
+
100
+ Then you can run tests like this:
101
+ `bundle exec rake`
102
+
103
+ == Contributing
104
+ The vmware_log_intelligence plugin project team welcomes contributions from the community. Before you start working with fluent-plugin-vmware-log-intelligence, please read our [Developer Certificate of Origin](https://cla.vmware.com/dco). All contributions to this repository must be signed as described on that page. Your signature certifies that you wrote the patch or have the right to pass it on as an open-source patch. For more detailed information, refer to [CONTRIBUTING.md](CONTRIBUTING.md).
105
+
106
+ == License
107
+ Please see LICENSE.txt[LICENSE.txt].
108
+
109
+ == Copyright
110
+
111
+ Copyright (c) 2013 ablagoev.
112
+
113
+ Copyright 2018 VMware, Inc.
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env rake
2
+
3
+ # Copyright (c) 2013 ablagoev
4
+ # Copyright 2018 VMware, Inc.
5
+ # SPDX-License-Identifier: MIT
6
+
7
+ require "bundler/gem_tasks"
8
+ require 'rake/testtask'
9
+
10
+ Rake::TestTask.new(:test) do |test|
11
+ test.libs << 'lib' << 'test'
12
+ test.pattern = 'test/**/test_*.rb'
13
+ test.verbose = true
14
+ end
15
+
16
+ task :default => :test
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 2.0.7
@@ -0,0 +1,53 @@
1
+ # Copyright (c) 2013 ablagoev
2
+ # Copyright 2018 VMware, Inc.
3
+ # SPDX-License-Identifier: MIT
4
+
5
+ $:.push File.expand_path('../lib', __FILE__)
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = 'fluent-plugin-vmware-aria-log-intelligence'
9
+ s.version = File.read("VERSION").strip
10
+ s.date = '2018-08-12'
11
+ s.summary = "Fluentd buffered output plugin for VMware Log Intelligence"
12
+ s.description = "Send Fluentd buffered logs to VMware Log Intelligence"
13
+ s.authors = ["Alexander Blagoev", "Chaur Wu"]
14
+ s.email = 'gwu@vmware.com'
15
+ s.homepage =
16
+ 'http://github.com/vmware/fluent-plugin-vmware-log-intelligence'
17
+
18
+ s.files = [
19
+ "lib/fluent/plugin/out_vmware_log_intelligence.rb",
20
+ "lib/fluent/plugin/http_client.rb",
21
+ "Gemfile",
22
+ "LICENSE.txt",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "fluent-plugin-vmware-log-intelligence.gemspec",
27
+ "test/helper.rb",
28
+ "test/plugin/test_out_vmware_log_intelligence.rb",
29
+ "test/plugin/test_http_client.rb",
30
+ ]
31
+
32
+ s.extra_rdoc_files = [
33
+ "LICENSE.txt",
34
+ "README.rdoc"
35
+ ]
36
+ s.licenses = ["MIT"]
37
+
38
+ s.require_paths = ['lib']
39
+
40
+ s.add_dependency "fluentd", ">= 0.14.20"
41
+ s.add_dependency "http", ">= 0.9.8"
42
+ s.add_dependency "myslog", "~> 0.0"
43
+ s.add_dependency "fluent-plugin-mysqlslowquery", ">= 0.0.9"
44
+ s.add_development_dependency "rake", ">= 0.9.2"
45
+ s.add_development_dependency "bundler", ">= 1.3.4"
46
+ s.add_development_dependency 'test-unit', '~> 3.1.0'
47
+ s.add_development_dependency 'webmock', '~> 3.4.0'
48
+ s.add_development_dependency 'fluent-plugin-detect-exceptions', '>= 0.0.12'
49
+ s.add_development_dependency 'fluent-plugin-concat', '>= 2.0.0'
50
+ s.add_development_dependency 'fluent-plugin-kubernetes_metadata_filter', '>= 2.0.0'
51
+ s.add_development_dependency 'fluent-plugin-multi-format-parser', '>= 1.0.0'
52
+ s.add_development_dependency 'fluent-plugin-postgres', '>= 0.0.1'
53
+ end
@@ -0,0 +1,72 @@
1
+ # Copyright 2018 VMware, Inc.
2
+ # SPDX-License-Identifier: MIT
3
+
4
+ module Fluent::Plugin
5
+ class HttpClient
6
+ def initialize(endpoint_url, verify_ssl,
7
+ headers, statuses, open_timeout, read_timeout, log)
8
+ @log = log
9
+ @statuses = statuses
10
+ @options = {}
11
+
12
+ if !verify_ssl
13
+ @log.warn('SSL verification of the remote VMware Log Intelligence service is turned off. This is serious security risk. Please turn on SSL verification and restart the Fluentd/td-agent process.')
14
+ ctx = OpenSSL::SSL::SSLContext.new
15
+ ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
16
+ @options = {:ssl_context => ctx}
17
+ end
18
+
19
+ timeout_options = {
20
+ :connect_timeout => open_timeout,
21
+ :read_timeout => read_timeout
22
+ }
23
+
24
+ @conn = HTTP.persistent(endpoint_url)
25
+ .headers(headers)
26
+ .timeout(timeout_options)
27
+
28
+ @endpoint_path = HTTP::URI.parse(endpoint_url).path
29
+ @last_429_time = nil
30
+ end
31
+
32
+ def check_quota
33
+ if @last_429_time
34
+ if (Time.new - @last_429_time) < 600
35
+ return false
36
+ end
37
+
38
+ @last_429_time = nil
39
+ end
40
+ return true
41
+ end
42
+
43
+ def post(data)
44
+ if !check_quota
45
+ return
46
+ end
47
+
48
+ begin
49
+ response = @conn.post(@endpoint_path, @options.merge(:body => data))
50
+ response.body.to_s
51
+ if (response.code == 429)
52
+ @log.warn('1GB quota of free account has been reached. Will stop sending data for 1 hour.')
53
+ @last_429_time = Time.new
54
+ else
55
+ @last_429_time = nil
56
+ end
57
+
58
+ if @statuses.include? response.code.to_i
59
+ # Raise an exception so that fluent will retry based on the configurations.
60
+ fail "Server returned bad status: #{response.code}. #{response.to_s}"
61
+ end
62
+
63
+ rescue EOFError, SystemCallError, OpenSSL::SSL::SSLError => e
64
+ @log.warn "http post raises exception: #{e.class}, '#{e.message}'"
65
+ end
66
+ end
67
+
68
+ def close
69
+ @conn.close
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,205 @@
1
+ # Copyright (c) 2013 ablagoev
2
+ # Copyright 2018 VMware, Inc.
3
+ # SPDX-License-Identifier: MIT
4
+
5
+
6
+ require 'zlib'
7
+ require "fluent/plugin/output"
8
+ require "fluent/plugin/http_client"
9
+
10
+ module Fluent::Plugin
11
+ class LogIntelligenceOutput < Output
12
+ Fluent::Plugin.register_output('vmware_log_intelligence', self)
13
+
14
+ config_param :http_compress, :bool, :default => false
15
+ config_param :endpoint_url, :string
16
+ config_param :bearer_token, :string, default: ''
17
+ config_param :http_retry_statuses, :string, default: ''
18
+ config_param :read_timeout, :integer, default: 60
19
+ config_param :open_timeout, :integer, default: 60
20
+ # config_param :use_ssl, :bool, :default => false
21
+ config_param :verify_ssl, :bool, :default => true
22
+ config_param :rate_limit_msec, :integer, :default => 0
23
+ # Keys from log event whose values should be added as log message/text
24
+ # to log-intelligence. Note these key/value pairs won't be added as metadata/fields
25
+ config_param :log_text_keys, :array, default: ["log", "message", "msg"], value_type: :string
26
+ # Flatten hashes to create one key/val pair w/o losing log data
27
+ config_param :flatten_hashes, :bool, :default => true
28
+ # Seperator to use for joining flattened keys
29
+ config_param :flatten_hashes_separator, :string, :default => "_"
30
+
31
+ config_section :buffer do
32
+ config_set_default :@type, "memory"
33
+ config_set_default :chunk_keys, []
34
+ config_set_default :timekey_use_utc, true
35
+ end
36
+
37
+ def initialize
38
+ super
39
+ require 'http'
40
+ require 'uri'
41
+ end
42
+
43
+ def validate_uri(uri_string)
44
+ unless uri_string =~ /^#{URI.regexp}$/
45
+ fail Fluent::ConfigError, 'endpoint_url invalid'
46
+ end
47
+
48
+ begin
49
+ @uri = URI.parse(uri_string)
50
+ rescue URI::InvalidURIError
51
+ raise Fluent::ConfigError, 'endpoint_url invalid'
52
+ end
53
+ end
54
+
55
+ def retrieve_headers(conf)
56
+ headers = {}
57
+ conf.elements.each do |element|
58
+ if @http_compress
59
+ set_gzip_header(element)
60
+ end
61
+ if element.name == 'headers'
62
+ if @bearer_token != ''
63
+ element['Authorization'] = 'Bearer ' + @bearer_token
64
+ end
65
+ headers = element.to_hash
66
+ end
67
+ end
68
+ headers
69
+ end
70
+
71
+ def set_gzip_header(element)
72
+ element['Content-Encoding'] = 'gzip'
73
+ element
74
+ end
75
+
76
+ def shorten_key(key)
77
+ # LINT doesn't allow some characters in field 'name'
78
+ # like '/', '-', '\', '.', etc. so replace them with @flatten_hashes_separator
79
+ key = key.gsub(/[\/\.\-\\]/,@flatten_hashes_separator).downcase
80
+ key
81
+ end
82
+
83
+ def create_lint_event(record)
84
+ flattened_records = {}
85
+ merged_records = {}
86
+ if @flatten_hashes
87
+ flattened_records = flatten_record(record, [])
88
+ else
89
+ flattened_records = record
90
+ end
91
+
92
+ keys = []
93
+ log = ''
94
+ flattened_records.each do |key, value|
95
+ begin
96
+ next if value.nil?
97
+ # LINT doesn't support duplicate fields, make unique names by appending underscore
98
+ key = shorten_key(key)
99
+ if keys.include?(key)
100
+ value = merged_records[key] + " " + value
101
+ end
102
+ keys.push(key)
103
+ key.force_encoding("utf-8")
104
+
105
+ if value.is_a?(String)
106
+ value.force_encoding("utf-8")
107
+ end
108
+ end
109
+
110
+ if @log_text_keys.include?(key)
111
+ if log != "#{value}"
112
+ if log.empty?
113
+ log = "#{value}"
114
+ else
115
+ log += " #{value}"
116
+ end
117
+ end
118
+ else
119
+ merged_records[key] = value
120
+ end
121
+ end
122
+ merged_records["text"] = log
123
+
124
+ if log == "\\n"
125
+ {}
126
+ else
127
+ merged_records
128
+ end
129
+ end
130
+
131
+ def flatten_record(record, prefix=[])
132
+ ret = {}
133
+
134
+ case record
135
+ when Hash
136
+ record.each do |key, value|
137
+ if @log_text_keys.include?(key)
138
+ ret.merge!({key.to_s => value})
139
+ else
140
+ ret.merge! flatten_record(value, prefix + [key.to_s])
141
+ end
142
+ end
143
+ when Array
144
+ record.each do |value|
145
+ ret.merge! flatten_record(value, prefix)
146
+ end
147
+ else
148
+ return {prefix.join(@flatten_hashes_separator) => record}
149
+ end
150
+ ret
151
+ end
152
+
153
+ def configure(conf)
154
+ super
155
+ validate_uri(@endpoint_url)
156
+
157
+ @statuses = @http_retry_statuses.split(',').map { |status| status.to_i }
158
+ @statuses = [] if @statuses.nil?
159
+
160
+ @headers = retrieve_headers(conf)
161
+
162
+ @http_client = Fluent::Plugin::HttpClient.new(
163
+ @endpoint_url, @verify_ssl, @headers, @statuses,
164
+ @open_timeout, @read_timeout, @log)
165
+ end
166
+
167
+ def multi_workers_ready?
168
+ true
169
+ end
170
+
171
+ def start
172
+ super
173
+ end
174
+
175
+ def shutdown
176
+ super
177
+ begin
178
+ @http_client.close if @http_client
179
+ rescue
180
+ end
181
+ end
182
+
183
+ def write(chunk)
184
+ is_rate_limited = (@rate_limit_msec != 0 and not @last_request_time.nil?)
185
+ if is_rate_limited and ((Time.now.to_f - @last_request_time) * 1000.0 < @rate_limit_msec)
186
+ @log.info('Dropped request due to rate limiting')
187
+ return
188
+ end
189
+
190
+ data = []
191
+ chunk.each do |time, record|
192
+ data << create_lint_event(record)
193
+ end
194
+
195
+ if @http_compress
196
+ gzip_body = Zlib::GzipWriter.new(StringIO.new)
197
+ gzip_body << data.to_json
198
+ @http_client.post(gzip_body.close.string)
199
+ else
200
+ @last_request_time = Time.now.to_f
201
+ @http_client.post(JSON.dump(data))
202
+ end
203
+ end
204
+ end
205
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,31 @@
1
+ # Copyright (c) 2013 ablagoev
2
+ # Copyright 2018 VMware, Inc.
3
+ # SPDX-License-Identifier: MIT
4
+
5
+ require 'coveralls'
6
+ Coveralls.wear!
7
+
8
+ require 'rubygems'
9
+ require 'bundler'
10
+
11
+ begin
12
+ Bundler.setup(:default, :development)
13
+ rescue Bundler::BundlerError => e
14
+ $stderr.puts e.message
15
+ $stderr.puts 'Run `bundle install` to install missing gems'
16
+ exit e.status_code
17
+ end
18
+
19
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
20
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
21
+
22
+ require 'test-unit'
23
+ require 'fluent/test'
24
+ require "fluent/test/driver/output"
25
+ require "fluent/test/helpers"
26
+ Test::Unit::TestCase.include(Fluent::Test::Helpers)
27
+ Test::Unit::TestCase.extend(Fluent::Test::Helpers)
28
+
29
+ require 'fluent/plugin/out_vmware_log_intelligence'
30
+ require 'webmock/test_unit'
31
+ WebMock.disable_net_connect!
@@ -0,0 +1,95 @@
1
+ # Copyright 2018 VMware, Inc.
2
+ # SPDX-License-Identifier: MIT
3
+
4
+ require 'helper'
5
+
6
+ class HttpClientTest < Test::Unit::TestCase
7
+ DEFAULT_URL = "https://local.endpoint:3000/dummy/xyz"
8
+
9
+ def stub_server_out_of_quota(url=DEFAULT_URL)
10
+ stub_request(:post, url).to_return(:status => [429, "User out of ingestion quota."])
11
+ end
12
+
13
+ def test_check_quota
14
+ http_client = create_http_client()
15
+ assert_equal http_client.check_quota, true
16
+
17
+ stub_server_out_of_quota
18
+ http_client.post(JSON.dump(sample_record()))
19
+ assert_equal http_client.check_quota, false
20
+ end
21
+
22
+ def stub_server_unavailable(url=DEFAULT_URL)
23
+ stub_request(:post, url).to_return(:status => [503, "Service Unavailable"])
24
+ end
25
+
26
+ def stub_server_returns_500(url=DEFAULT_URL)
27
+ stub_request(:post, url).to_return(:status => [500, "Internal service error"])
28
+ end
29
+
30
+ def stub_server_raise_error(url=DEFAULT_URL)
31
+ stub_request(:post, url).with do |req|
32
+ raise IOError
33
+ end
34
+ end
35
+
36
+ def stub_post_logs(url=DEFAULT_URL)
37
+ stub_request(:post, url)
38
+ .with(
39
+ body: "[{\"field1\":26,\"field2\":\"value26\"},{\"field1\":27,\"field2\":\"value27\"},{\"field1\":28,\"field2\":\"value28\"}]",
40
+ headers: {
41
+ 'Authorization'=>'Bearer EdaNNN68y',
42
+ 'Connection'=>'Keep-Alive',
43
+ 'Content-Type'=>'application/json',
44
+ 'Format'=>'syslog',
45
+ 'Structure'=>'simple'
46
+ })
47
+ .to_return(:status => 200, :body => "ok")
48
+ end
49
+
50
+ def sample_record()
51
+ [
52
+ {'field1' => 26, 'field2' => 'value26'},
53
+ {'field1' => 27, 'field2' => 'value27'},
54
+ {'field1' => 28, 'field2' => 'value28'}
55
+ ]
56
+ end
57
+
58
+ def create_http_client
59
+ Fluent::Plugin::HttpClient.new(
60
+ DEFAULT_URL, true,
61
+ {
62
+ 'Authorization'=>'Bearer EdaNNN68y',
63
+ 'Connection'=>'Keep-Alive',
64
+ 'Content-Type'=>'application/json',
65
+ 'Format'=>'syslog',
66
+ 'Host'=>'local.endpoint:3000',
67
+ 'Structure'=>'simple'
68
+ }, [500, 510], 60, 60, Logger.new(STDOUT))
69
+ end
70
+
71
+ def test_retry_on_response_status_code
72
+ http_client = create_http_client()
73
+ stub_server_returns_500
74
+ assert_raise RuntimeError do
75
+ http_client.post(JSON.dump(sample_record()))
76
+ end
77
+ end
78
+
79
+ def test_server_raise_error
80
+ http_client = create_http_client()
81
+ stub_server_raise_error
82
+ assert_raise IOError do
83
+ http_client.post(JSON.dump(sample_record()))
84
+ end
85
+ end
86
+
87
+ def test_post_logs
88
+ stub_post_logs
89
+ http_client = create_http_client()
90
+ http_client.post(JSON.dump(sample_record()))
91
+
92
+ stub_post_logs
93
+ http_client.post(JSON.dump(sample_record()))
94
+ end
95
+ end
@@ -0,0 +1,154 @@
1
+ # Copyright 2018 VMware, Inc.
2
+ # SPDX-License-Identifier: MIT
3
+
4
+ require 'helper'
5
+ require 'yaml'
6
+
7
+ class LogIntelligenceOutputTest < Test::Unit::TestCase
8
+ def setup
9
+ Fluent::Test.setup
10
+ end
11
+
12
+ def create_driver(conf)
13
+ Fluent::Test::Driver::Output.new(Fluent::Plugin::LogIntelligenceOutput).configure(conf)
14
+ end
15
+
16
+ def test_configure
17
+ config = %[
18
+ endpoint_url http://localhost:9200/li
19
+ ]
20
+
21
+ instance = create_driver(config).instance
22
+ assert_equal 'http://localhost:9200/li', instance.endpoint_url
23
+ assert_equal '', instance.instance_eval { @http_retry_statuses }
24
+ assert_equal [], instance.instance_eval { @statuses }
25
+ assert_equal 60, instance.instance_eval { @read_timeout }
26
+ assert_equal 60, instance.instance_eval { @open_timeout }
27
+ assert_equal({}, instance.instance_eval { @headers })
28
+ end
29
+
30
+ def test_full_configure
31
+ config = %[
32
+ @type http_buffered
33
+ endpoint_url https://local.endpoint:3000/dummy/xyz
34
+ verify_ssl false
35
+ <headers>
36
+ Content-Type application/json
37
+ Authorization Bearer EdaNNN68y
38
+ structure simple
39
+ format syslog
40
+ </headers>
41
+ <buffer>
42
+ chunk_limit_records 3
43
+ flush_interval 12s
44
+ retry_max_times 3
45
+ </buffer>
46
+ ]
47
+
48
+ instance = create_driver(config).instance
49
+ assert_equal 'https://local.endpoint:3000/dummy/xyz', instance.endpoint_url
50
+ assert_equal '', instance.http_retry_statuses
51
+ assert_equal [], instance.instance_eval { @statuses }
52
+ assert_equal 60, instance.read_timeout
53
+ assert_equal 60, instance.open_timeout
54
+ assert_equal({
55
+ "Authorization"=>"Bearer EdaNNN68y",
56
+ "Content-Type"=>"application/json",
57
+ "structure"=>"simple",
58
+ "format"=>"syslog"}, instance.instance_eval { @headers })
59
+ end
60
+
61
+ def test_invalid_endpoint
62
+ assert_raise Fluent::ConfigError do
63
+ create_driver('endpoint_url \\@3')
64
+ end
65
+
66
+ assert_raise Fluent::ConfigError do
67
+ create_driver('endpoint_url google.com')
68
+ end
69
+ end
70
+
71
+ def test_json_with_special_char1
72
+ $log = Logger.new(STDOUT)
73
+ $log.level = Logger::DEBUG
74
+ input = {"host" => "192.168.0.1", "log" => "2019-12-02 02:04:13.556787 I | mvcc: finished scheduled compaction at 2259 (took 822.573\xC2\xB5s)"}
75
+ output = [{"host" => "192.168.0.1", "text" => "2019-12-02 02:04:13.556787 I | mvcc: finished scheduled compaction at 2259 (took 822.573\xC2\xB5s)"}]
76
+ $log.debug("INPUT: #{input}")
77
+ $log.debug("OUTPUT: #{output}")
78
+ verify_write(input, output)
79
+ end
80
+
81
+ def test_json_with_special_char2
82
+ $log = Logger.new(STDOUT)
83
+ $log.level = Logger::DEBUG
84
+ input = {"host" => "192.168.0.1", "log" => "2019-12-02 02:04:13.556787 I | mvcc: finished scheduled compaction at 2259 (took 678.559µs)"}
85
+ output = [{"host" => "192.168.0.1", "text" => "2019-12-02 02:04:13.556787 I | mvcc: finished scheduled compaction at 2259 (took 678.559µs)"}]
86
+ $log.debug("INPUT: #{input}")
87
+ $log.debug("OUTPUT: #{output}")
88
+ verify_write(input, output)
89
+ end
90
+
91
+ def test_json_with_log
92
+ input = {"host" => "192.168.0.1", "log" => "machine reboot"}
93
+ output = [{"host" => "192.168.0.1", "text" => "machine reboot"}]
94
+ verify_write(input, output)
95
+ end
96
+
97
+ def test_json_with_msg
98
+ input = {"host" => "192.168.0.1", "msg" => "machine reboot"}
99
+ output = [{"host" => "192.168.0.1", "text" => "machine reboot"}]
100
+ verify_write(input, output)
101
+ end
102
+
103
+ def test_nested_json
104
+ input = {"host" => "192.168.0.1", "properties" => {"type" => "windows"}, "msg" => "machine reboot"}
105
+ output = [{"host" => "192.168.0.1", "properties_type" => "windows", "text" => "machine reboot"}]
106
+ verify_write(input, output)
107
+ end
108
+
109
+ def test_nested_json_with_log
110
+ input = {"host" => "192.168.0.1", "properties" => {"type" => "windows"}, "message" => "machine reboot"}
111
+ output = [{"host" => "192.168.0.1", "properties_type" => "windows", "text" => "machine reboot"}]
112
+ verify_write(input, output)
113
+ end
114
+
115
+ def test_json_with_new_line
116
+ input = {"host" => "192.168.0.1", "log" => "\\n"}
117
+ output = [{}]
118
+ verify_write(input, output)
119
+ end
120
+
121
+ def test_json_with_multiple_log_formats
122
+ input = {"host" => "192.168.0.1", "log" => "custom log:1", "message" => "custom message:2", "msg" => "custom msg:3"}
123
+ output = [{"host" => "192.168.0.1", "text" => "custom log:1 custom message:2 custom msg:3"}]
124
+ verify_write(input, output)
125
+ end
126
+
127
+ # For any null values, its key and values are not being populated to output.
128
+ def test_json_with_null_value
129
+ input = {"host" => "192.168.0.1", "source" => nil, "log" => "abc"}
130
+ output = [{"host" => "192.168.0.1", "text" => "abc"}]
131
+ verify_write(input, output)
132
+ end
133
+
134
+ # like '/', '-', '\', '.', etc. replace with '_'
135
+ def test_json_with_sperators
136
+ input = {"host" => "192.168.0.1", "/properties" => {"type-" => "windows"}, "msg" => "123"}
137
+ output = [{"host" => "192.168.0.1", "_properties_type_" => "windows", "text" => "123"}]
138
+ verify_write(input, output)
139
+ end
140
+
141
+ def verify_write(input, output)
142
+ config = %[
143
+ endpoint_url http://localhost:9200/li
144
+ ]
145
+ stub = stub_request(:post, "http://localhost:9200/li").with(body: output.to_json).
146
+ to_return(status: 200, body: "", headers: {})
147
+
148
+ driver = create_driver(config)
149
+ driver.run(default_tag: 'test') do
150
+ driver.feed(input)
151
+ end
152
+ assert_requested(stub)
153
+ end
154
+ end
metadata ADDED
@@ -0,0 +1,238 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fluent-plugin-vmware-aria-log-intelligence
3
+ version: !ruby/object:Gem::Version
4
+ version: 2.0.7
5
+ platform: ruby
6
+ authors:
7
+ - Alexander Blagoev
8
+ - Chaur Wu
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2018-08-12 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: fluentd
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: 0.14.20
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: 0.14.20
28
+ - !ruby/object:Gem::Dependency
29
+ name: http
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: 0.9.8
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: 0.9.8
42
+ - !ruby/object:Gem::Dependency
43
+ name: myslog
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '0.0'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '0.0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: fluent-plugin-mysqlslowquery
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 0.0.9
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: 0.0.9
70
+ - !ruby/object:Gem::Dependency
71
+ name: rake
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: 0.9.2
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: 0.9.2
84
+ - !ruby/object:Gem::Dependency
85
+ name: bundler
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: 1.3.4
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: 1.3.4
98
+ - !ruby/object:Gem::Dependency
99
+ name: test-unit
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - "~>"
103
+ - !ruby/object:Gem::Version
104
+ version: 3.1.0
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - "~>"
110
+ - !ruby/object:Gem::Version
111
+ version: 3.1.0
112
+ - !ruby/object:Gem::Dependency
113
+ name: webmock
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - "~>"
117
+ - !ruby/object:Gem::Version
118
+ version: 3.4.0
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - "~>"
124
+ - !ruby/object:Gem::Version
125
+ version: 3.4.0
126
+ - !ruby/object:Gem::Dependency
127
+ name: fluent-plugin-detect-exceptions
128
+ requirement: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: 0.0.12
133
+ type: :development
134
+ prerelease: false
135
+ version_requirements: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: 0.0.12
140
+ - !ruby/object:Gem::Dependency
141
+ name: fluent-plugin-concat
142
+ requirement: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: 2.0.0
147
+ type: :development
148
+ prerelease: false
149
+ version_requirements: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: 2.0.0
154
+ - !ruby/object:Gem::Dependency
155
+ name: fluent-plugin-kubernetes_metadata_filter
156
+ requirement: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - ">="
159
+ - !ruby/object:Gem::Version
160
+ version: 2.0.0
161
+ type: :development
162
+ prerelease: false
163
+ version_requirements: !ruby/object:Gem::Requirement
164
+ requirements:
165
+ - - ">="
166
+ - !ruby/object:Gem::Version
167
+ version: 2.0.0
168
+ - !ruby/object:Gem::Dependency
169
+ name: fluent-plugin-multi-format-parser
170
+ requirement: !ruby/object:Gem::Requirement
171
+ requirements:
172
+ - - ">="
173
+ - !ruby/object:Gem::Version
174
+ version: 1.0.0
175
+ type: :development
176
+ prerelease: false
177
+ version_requirements: !ruby/object:Gem::Requirement
178
+ requirements:
179
+ - - ">="
180
+ - !ruby/object:Gem::Version
181
+ version: 1.0.0
182
+ - !ruby/object:Gem::Dependency
183
+ name: fluent-plugin-postgres
184
+ requirement: !ruby/object:Gem::Requirement
185
+ requirements:
186
+ - - ">="
187
+ - !ruby/object:Gem::Version
188
+ version: 0.0.1
189
+ type: :development
190
+ prerelease: false
191
+ version_requirements: !ruby/object:Gem::Requirement
192
+ requirements:
193
+ - - ">="
194
+ - !ruby/object:Gem::Version
195
+ version: 0.0.1
196
+ description: Send Fluentd buffered logs to VMware Log Intelligence
197
+ email: gwu@vmware.com
198
+ executables: []
199
+ extensions: []
200
+ extra_rdoc_files:
201
+ - LICENSE.txt
202
+ - README.rdoc
203
+ files:
204
+ - Gemfile
205
+ - LICENSE.txt
206
+ - README.rdoc
207
+ - Rakefile
208
+ - VERSION
209
+ - fluent-plugin-vmware-log-intelligence.gemspec
210
+ - lib/fluent/plugin/http_client.rb
211
+ - lib/fluent/plugin/out_vmware_log_intelligence.rb
212
+ - test/helper.rb
213
+ - test/plugin/test_http_client.rb
214
+ - test/plugin/test_out_vmware_log_intelligence.rb
215
+ homepage: http://github.com/vmware/fluent-plugin-vmware-log-intelligence
216
+ licenses:
217
+ - MIT
218
+ metadata: {}
219
+ post_install_message:
220
+ rdoc_options: []
221
+ require_paths:
222
+ - lib
223
+ required_ruby_version: !ruby/object:Gem::Requirement
224
+ requirements:
225
+ - - ">="
226
+ - !ruby/object:Gem::Version
227
+ version: '0'
228
+ required_rubygems_version: !ruby/object:Gem::Requirement
229
+ requirements:
230
+ - - ">="
231
+ - !ruby/object:Gem::Version
232
+ version: '0'
233
+ requirements: []
234
+ rubygems_version: 3.0.3.1
235
+ signing_key:
236
+ specification_version: 4
237
+ summary: Fluentd buffered output plugin for VMware Log Intelligence
238
+ test_files: []