logstash-output-newrelic 1.2.2 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '068a9ac838ba356ffc7cc6746933ad3970d5c72df54c6633e1c7453283d700c0'
4
- data.tar.gz: 8f6557b7b8ab66e4d2de7d772cbf470403be8f125d2bb90d371c0af4581ec84e
3
+ metadata.gz: 9e6c4d247631f976e7a7fc6153a02cc764f583491c749654a827c1fd6846c6c2
4
+ data.tar.gz: 42389a3c04b5f1ef4804f9bf7efa820d7037c25e2df73c127354d180c624ff02
5
5
  SHA512:
6
- metadata.gz: c200662a9b6e2157193f9d21c7dadc9562db4ca0245525775742b5518afa73b7db801dc05b506c8a5c4df74e127c7c8e520a684b5a9cf709f29350c09c0b9ebc
7
- data.tar.gz: b6f15e63a3a625fc86c4dc6423b37973a01ae8406f31aa944aac62c3343d7c7dcfb3a7642f54d0464c170b824f59126d5b25b87a459cea1c79f2d14e9fd087ab
6
+ metadata.gz: deebd2f6b0c7f5c07ee4fc347f577a9011a481be03f8e170a9e9c368594009cbc33b853e05c025a1938b038febc8cc6d6db8c906aa3eacbeacfabab3483d7755
7
+ data.tar.gz: becf3aef35efc785f84ea956cabf2004d8d1ddbde4dac11f11bec3a25b65ab9a3277a97dcb35add2839581fb0b19ddf59114a83c9e5afd7a54da14ddd9b181d1
data/DEVELOPER.md CHANGED
@@ -2,12 +2,23 @@
2
2
 
3
3
  # Getting started
4
4
 
5
- * Install JRuby: `rbenv install jruby-9.2.5.0`
6
- * Use that JRuby: `rbenv local jruby-9.2.5.0`
5
+ **NOTE for Mac M1 users: ** note that `jruby-9.3.3.0` is the first jruby compatible with Mac M1 processors. In order to
6
+ develop the plugin locally, we recommend using this version. Nevertheless, when building the gem file (in the GH workflows),
7
+ we keep using jruby 9.2.13.0 in order to be backwards-compatible with older Logstash versions.
8
+
9
+ * Install RVM:
10
+ * `command curl -sSL https://rvm.io/pkuczynski.asc | gpg --import -`
11
+ * `\curl -sSL https://get.rvm.io | bash -s stable`
12
+ * Reopen the terminal for `rvm` command to be available
13
+ * Install JRuby: `rvm install jruby-9.2.13.0`.
14
+ * Use that JRuby: `rvm use jruby-9.2.13.0`
15
+ * Ensure your terminal is using Java 11.
7
16
  * Install Bundler gem: `jruby -S gem install bundler`
8
17
 
9
18
  # Developing
10
19
 
20
+ * Ensure you have `logstash` installed locally (required for unit testing): `brew install logstash`
21
+ * Ensure your `logstash` path matches the one in `Gemfile`
11
22
  * Install dependencies: `jruby -S bundle install`
12
23
  * Write tests and production code!
13
24
  * Bump version: edit version file `version.rb`
data/Gemfile CHANGED
@@ -1,3 +1,15 @@
1
1
  source 'https://rubygems.org'
2
2
  gemspec
3
3
 
4
+ # The following is required to locally develop this plugin. Note that this Gemfile is NOT used when building the gem
5
+ # file for this plugin (see merge-to-master.yml), only when unit testing. When unit-testing, we need to have logstash-core
6
+ # in our local machine, given that the logstash-core GEM has not been published since 5.6.0.
7
+ # See: https://github.com/elastic/logstash/pull/14229 https://github.com/elastic/logstash/issues/14203
8
+ # And: https://github.com/elastic/logstash/pull/14229
9
+
10
+ logstash_path = ENV['LOGSTASH_PATH'] || '/opt/homebrew/Cellar/logstash/8.9.0/libexec'
11
+
12
+ if Dir.exist?(logstash_path)
13
+ gem 'logstash-core', :path => "#{logstash_path}/logstash-core"
14
+ gem 'logstash-core-plugin-api', :path => "#{logstash_path}/logstash-core-plugin-api"
15
+ end
data/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ [![Community Plus header](https://github.com/newrelic/opensource-website/raw/master/src/images/categories/Community_Plus.png)](https://opensource.newrelic.com/oss-category/#community-plus)
2
+
1
3
  # New Relic Logstash Output Plugin
2
4
 
3
5
  This is a plugin for [Logstash](https://github.com/elastic/logstash) that outputs logs to New Relic.
@@ -65,7 +67,7 @@ When using this plugin in the EU override the base_uri with `https://log-api.eu.
65
67
 
66
68
  ## Testing
67
69
 
68
- An easy way to test the plugin is to make sure Logstash is getting input from a log file you
70
+ An easy way to test the plugin is to make sure Logstash is getting input from a log file you
69
71
  can write to. Something like this in your logstash.conf:
70
72
  ```
71
73
  input {
@@ -77,3 +79,18 @@ input {
77
79
  * Restart Logstash
78
80
  * Append a test log message to your log file: `echo "test message" >> /path/to/your/log/file`
79
81
  * Search New Relic Logs for `"test message"`
82
+
83
+ ## Community
84
+
85
+ New Relic hosts and moderates an online forum where customers can interact with New Relic employees as well as other customers to get help and share best practices. Like all official New Relic open source projects, there's a related Community topic in the New Relic Explorers Hub: [Log forwarding](https://discuss.newrelic.com/tag/log-forwarding)
86
+
87
+ ## A note about vulnerabilities
88
+
89
+ As noted in our [security policy](../../security/policy), New Relic is committed to the privacy and security of our customers and their data. We believe that providing coordinated disclosure by security researchers and engaging with the security community are important means to achieve our security goals.
90
+
91
+ If you believe you have found a security vulnerability in this project or any of New Relic's products or websites, we welcome and greatly appreciate you reporting it to New Relic through [HackerOne](https://hackerone.com/newrelic).
92
+
93
+ If you would like to contribute to this project, review [these guidelines](https://opensource.newrelic.com/code-of-conduct/).
94
+
95
+ ## License
96
+ logstash-output-plugin is licensed under the [Apache 2.0](http://apache.org/licenses/LICENSE-2.0.txt) License.
@@ -12,10 +12,11 @@ require_relative './exception/error'
12
12
 
13
13
  class LogStash::Outputs::NewRelic < LogStash::Outputs::Base
14
14
  java_import java.util.concurrent.Executors;
15
- java_import java.util.concurrent.Semaphore;
16
15
 
17
16
  NON_RETRYABLE_CODES = Set[401, 403]
18
17
 
18
+ MAX_PAYLOAD_SIZE_BYTES = 1_000_000
19
+
19
20
  config_name "newrelic"
20
21
 
21
22
  config :api_key, :validate => :password, :required => false
@@ -23,6 +24,8 @@ class LogStash::Outputs::NewRelic < LogStash::Outputs::Base
23
24
  config :concurrent_requests, :validate => :number, :default => 1
24
25
  config :base_uri, :validate => :string, :default => "https://log-api.newrelic.com/log/v1"
25
26
  config :max_retries, :validate => :number, :default => 3
27
+ # Only used for E2E testing
28
+ config :custom_ca_cert, :validate => :string, :required => false
26
29
 
27
30
  public
28
31
 
@@ -37,10 +40,10 @@ class LogStash::Outputs::NewRelic < LogStash::Outputs::Base
37
40
  }
38
41
  @header = {
39
42
  'X-Event-Source' => 'logs',
40
- 'Content-Encoding' => 'gzip'
43
+ 'Content-Encoding' => 'gzip',
44
+ 'Content-Type' => 'application/json'
41
45
  }.merge(auth).freeze
42
46
  @executor = java.util.concurrent.Executors.newFixedThreadPool(@concurrent_requests)
43
- @semaphor = java.util.concurrent.Semaphore.new(@concurrent_requests)
44
47
  end
45
48
 
46
49
  # Used by tests so that the test run can complete (background threads prevent JVM exit)
@@ -64,34 +67,40 @@ class LogStash::Outputs::NewRelic < LogStash::Outputs::Base
64
67
  end
65
68
  end
66
69
 
67
- def encode(event_hash)
68
- log_message_hash = {
69
- # non-intrinsic attributes get put into 'attributes'
70
- :attributes => event_hash
71
- }
70
+ def to_nr_logs(logstash_events)
71
+ logstash_events.map do |logstash_event|
72
+ event_hash = logstash_event.to_hash
72
73
 
73
- # intrinsic attributes go at the top level
74
- if event_hash['message']
75
- log_message_hash['message'] = event_hash['message']
76
- log_message_hash[:attributes].delete('message')
77
- end
78
- if event_hash['timestamp']
79
- log_message_hash['timestamp'] = event_hash['timestamp']
80
- log_message_hash[:attributes].delete('timestamp')
81
- end
74
+ nr_log_message_hash = {
75
+ # non-intrinsic attributes get put into 'attributes'
76
+ :attributes => event_hash
77
+ }
82
78
 
83
- log_message_hash
79
+ # intrinsic attributes go at the top level
80
+ if event_hash['message']
81
+ nr_log_message_hash['message'] = event_hash['message']
82
+ nr_log_message_hash[:attributes].delete('message')
83
+ end
84
+ if event_hash['timestamp']
85
+ nr_log_message_hash['timestamp'] = event_hash['timestamp']
86
+ nr_log_message_hash[:attributes].delete('timestamp')
87
+ end
88
+
89
+ nr_log_message_hash
90
+ end
84
91
  end
85
92
 
86
- def multi_receive(events)
87
- if events.size == 0
93
+ def multi_receive(logstash_events)
94
+ if logstash_events.empty?
88
95
  return
89
96
  end
90
97
 
91
- payload = []
92
- events.each do |event|
93
- payload.push(encode(event.to_hash))
94
- end
98
+ nr_logs = to_nr_logs(logstash_events)
99
+
100
+ package_and_send_recursively(nr_logs)
101
+ end
102
+
103
+ def package_and_send_recursively(nr_logs)
95
104
  payload = {
96
105
  :common => {
97
106
  :attributes => {
@@ -101,19 +110,29 @@ class LogStash::Outputs::NewRelic < LogStash::Outputs::Base
101
110
  }
102
111
  }
103
112
  },
104
- :logs => payload
113
+ :logs => nr_logs
105
114
  }
106
- @semaphor.acquire()
115
+
107
116
  execute = @executor.java_method :submit, [java.lang.Runnable]
108
117
  execute.call do
109
- begin
110
- io = StringIO.new
111
- gzip = Zlib::GzipWriter.new(io)
112
- gzip << [payload].to_json
113
- gzip.close
114
- nr_send(io.string)
115
- ensure
116
- @semaphor.release()
118
+ compressed_payload = StringIO.new
119
+ gzip = Zlib::GzipWriter.new(compressed_payload)
120
+ gzip << [payload].to_json
121
+ gzip.close
122
+
123
+ compressed_size = compressed_payload.string.bytesize
124
+ log_record_count = nr_logs.length
125
+
126
+ if compressed_size >= MAX_PAYLOAD_SIZE_BYTES && log_record_count == 1
127
+ @logger.error("Can't compress record below required maximum packet size and it will be discarded.")
128
+ elsif compressed_size >= MAX_PAYLOAD_SIZE_BYTES && log_record_count > 1
129
+ @logger.debug("Compressed payload size (#{compressed_size}) exceededs maximum packet size (1MB) and will be split in two.")
130
+ split_index = log_record_count / 2
131
+ package_and_send_recursively(nr_logs[0...split_index])
132
+ package_and_send_recursively(nr_logs[split_index..-1])
133
+ else
134
+ @logger.debug("Payload compressed size: #{compressed_size}")
135
+ nr_send(compressed_payload.string)
117
136
  end
118
137
  end
119
138
  end
@@ -131,6 +150,12 @@ class LogStash::Outputs::NewRelic < LogStash::Outputs::Base
131
150
  request = Net::HTTP::Post.new(@end_point.request_uri)
132
151
  http.use_ssl = true
133
152
  http.verify_mode = OpenSSL::SSL::VERIFY_PEER
153
+ if !@custom_ca_cert.nil?
154
+ store = OpenSSL::X509::Store.new
155
+ ca_cert = OpenSSL::X509::Certificate.new(File.read(@custom_ca_cert))
156
+ store.add_cert(ca_cert)
157
+ http.cert_store = store
158
+ end
134
159
  @header.each { |k, v| request[k] = v }
135
160
  request.body = payload
136
161
  handle_response(http.request(request))
@@ -1,7 +1,7 @@
1
1
  module LogStash
2
2
  module Outputs
3
3
  module NewRelicVersion
4
- VERSION = "1.2.2"
4
+ VERSION = "1.4.0"
5
5
  end
6
6
  end
7
7
  end