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 +4 -4
- data/DEVELOPER.md +13 -2
- data/Gemfile +12 -0
- data/README.md +18 -1
- data/lib/logstash/outputs/newrelic.rb +59 -34
- data/lib/logstash/outputs/newrelic_version/version.rb +1 -1
- data/spec/outputs/input_17997_messages_resulting_in_2680KB_compressed_payload.json +17997 -0
- data/spec/outputs/input_5000_messages_resulting_in_740KB_compressed_payload.json +5000 -0
- data/spec/outputs/newrelic_spec.rb +94 -0
- data/spec/outputs/single_input_message_exceeeding_1MB_once_compressed.json +1 -0
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9e6c4d247631f976e7a7fc6153a02cc764f583491c749654a827c1fd6846c6c2
|
4
|
+
data.tar.gz: 42389a3c04b5f1ef4804f9bf7efa820d7037c25e2df73c127354d180c624ff02
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
6
|
-
|
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
|
68
|
-
|
69
|
-
|
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
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
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
|
-
|
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(
|
87
|
-
if
|
93
|
+
def multi_receive(logstash_events)
|
94
|
+
if logstash_events.empty?
|
88
95
|
return
|
89
96
|
end
|
90
97
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
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 =>
|
113
|
+
:logs => nr_logs
|
105
114
|
}
|
106
|
-
|
115
|
+
|
107
116
|
execute = @executor.java_method :submit, [java.lang.Runnable]
|
108
117
|
execute.call do
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
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))
|