logstash-output-newrelic 1.1.1 → 1.2.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 45a98fd90dd92f095b34d4593459e6b5587ab779c331597c291ea3b94ad4291e
4
- data.tar.gz: f7f612da2d0040da95ca329c25e6dae097762246f5fa5061ad01082d22df0733
3
+ metadata.gz: a4569ef6cca71b35b48be2b7ad4f42c5648368a082ec71af161c4024d841df53
4
+ data.tar.gz: d8e2e7397ddc36ad08710f4dbe67b4cfb6f795ca6fa379502991bc0323673169
5
5
  SHA512:
6
- metadata.gz: 816c2f5fa309328ffdf78f58894885cf791c3f8fb91867620e8494e1761e2ca4b8d9744b4c6a385c13482a7654ecfc677fb0792dc4174141e0b92beb6bc757ec
7
- data.tar.gz: b2b41b31f4af73a28bdad58416c10f0a25e54a56bc295987d96c825268bce4bcfcdc01e80ca0027dfaa71af95edb9c88d8fe254ad5be6aa6b025b692742d50b1
6
+ metadata.gz: accb5c5d6ee856dc360e0522ac052cffee7e65ab8808e0bc19a588ad247ae7bb0c1132d96e105afc520560ebcdb85033af290a3af1b96cdb838623535f2510bc
7
+ data.tar.gz: cec14c1c4e073bba5533cc77a60b9abe4b85d5cde2a6cad8a563140273ac965a02d02e26e778bbab6285027ebd9123f58e7f94b7f5b5fca6d4e240d27432c48e
data/DEVELOPER.md CHANGED
@@ -14,14 +14,6 @@
14
14
  * Run tests: `jruby -S bundle exec rspec`
15
15
  * Build the gem: `jruby -S gem build logstash-output-newrelic.gemspec`
16
16
 
17
- ## Pushing changes to the public repo
18
- After updating the New Relic repo with changes, changes will need to be pushed to the public GitHub repo at: https://github.com/newrelic/logstash-output-plugin
19
-
20
- * `git remote add public git@github.com:newrelic/logstash-output-plugin.git`
21
- * `git push public master:name-of-branch-to-create`
22
- * Create a PR from that branch in https://github.com/newrelic/logstash-output-plugin
23
- * Get the PR reviewed, merged, and delete the branch!
24
-
25
17
  # Testing it with a local Logstash install
26
18
 
27
19
  Note: you may need to run the following commands outside of your checkout, since these should not
@@ -31,10 +23,4 @@ be run with the JRuby version that you've configured your checkout to use (by us
31
23
  * Add new version: `logstash-plugin install logstash-output-newrelic-<version>.gem`
32
24
  * Restart logstash: For Homebrew: `brew services restart logstash`
33
25
  * Cause a change that you've configured Logstash to pick up (for instance, append to a file you're having it monitor)
34
- * Look in `https://staging-one.newrelic.com/launcher/logger.log-launcher` for your log message
35
-
36
- # Push changes to RubyGems
37
- After updating the source code and gem version in `version.rb`, push the changes to RubyGems. There is an older version of the gem that we do not want to overwrite — `version 0.9.1`. Please be sure you are publishing changes to the correct gem i.e. `version 1.0.0` or higher. Note, you must be a gem owner to publish changes on [RubyGems.org](https://rubygems.org/profiles/NR-LOGGING). Once you've created the account, you will need to run `gem signin` to login to RubyGems via the command line.
38
-
39
- * Build the gem: `gem build logstash-output-newrelic.gemspec`
40
- * Publish the gem: `gem push logstash-output-newrelic-<VERSION>.gem` with the updated version (ex: `gem push logstash-output-newrelic-1.0.0.gem`)
26
+ * Look in `https://one.newrelic.com/launcher/logger.log-launcher` for your log message
data/README.md CHANGED
@@ -11,9 +11,11 @@ Versions of this plugin less than 1.0.0 are unsupported.
11
11
 
12
12
  ## Configuration
13
13
 
14
- Add the following block to your logstash.conf (with your specific API Insert key), then restart Logstash.
14
+ Add one of the following blocks to your logstash.conf (with your specific key), then restart Logstash.
15
15
  There are other optional configuration properties, see below.
16
16
 
17
+ ### Using API Insert Key
18
+
17
19
  Get your API Insert Key:
18
20
  `https://insights.newrelic.com/accounts/<ACCOUNT_ID>/manage/api_keys`
19
21
 
@@ -26,12 +28,28 @@ output {
26
28
  }
27
29
  ```
28
30
 
31
+ ### Using License Key
32
+
33
+ Get your License Key:
34
+ `https://rpm.newrelic.com/accounts/<ACCOUNT_ID>`
35
+
36
+ Example:
37
+ ```rb
38
+ output {
39
+ newrelic {
40
+ license_key => "<LICENSE_KEY>"
41
+ }
42
+ }
43
+ ```
29
44
 
30
45
  ### Required plugin configuration
31
46
 
47
+ Exactly one of the following:
48
+
32
49
  | Property | Description |
33
50
  |---|---|
34
51
  | api_key | your New Relic API Insert key |
52
+ | license_key | your New Relic License key |
35
53
 
36
54
  ### Optional plugin configuration
37
55
 
@@ -39,11 +57,11 @@ output {
39
57
  |---|---|---|
40
58
  | concurrent_requests | The number of threads to make requests from | 1 |
41
59
  | base_uri | New Relic ingestion endpoint | https://log-api.newrelic.com/log/v1 |
42
-
60
+ | max_retries | Maximum number attempts to retry to send a message. If set to 0, no re-attempts will be made. | 3 |
43
61
 
44
62
  ### EU plugin configuration
45
63
 
46
- When using this plugin in the EU override the base_uri with `http://log-api.eu.newrelic.com/log/v1`
64
+ When using this plugin in the EU override the base_uri with `https://log-api.eu.newrelic.com/log/v1`
47
65
 
48
66
  ## Testing
49
67
 
@@ -59,30 +77,3 @@ input {
59
77
  * Restart Logstash
60
78
  * Append a test log message to your log file: `echo "test message" >> /path/to/your/log/file`
61
79
  * Search New Relic Logs for `"test message"`
62
-
63
- ## JSON message parsing
64
-
65
- This plugin will attempt to parse any 'message' attribute as JSON -- if it is JSON, it will be parsed and
66
- the JSON attributes will be added to the event.
67
-
68
- For example, the event:
69
- ```
70
- {
71
- "timestamp": 1562767499238,
72
- "message": "{\"service-name\": \"login-service\", \"user\": {\"id\": 123, \"name\": \"alice\"}}"
73
- }
74
-
75
- ```
76
-
77
- Will be treated as:
78
- ```
79
- {
80
- "timestamp": 1562767499238,
81
- "message": "{\"service-name\": \"my-service\", \"user\": {\"id\": 123, \"name\": \"alice\"}}",
82
- "service-name": "login-service",
83
- "user": {
84
- "id": 123,
85
- "name": "alice"
86
- }
87
- }
88
- ```
@@ -0,0 +1,24 @@
1
+ require 'bigdecimal'
2
+
3
+ class BigDecimal
4
+ # Floating-point numbers that go through the 'json' Logstash filter get automatically converted into BigDecimals.
5
+ # Example of such a filter:
6
+ #
7
+ # filter {
8
+ # json {
9
+ # source => "message"
10
+ # }
11
+ # }
12
+ #
13
+ # The problem is that { "value" => BigDecimal('0.12345') } gets serialized into { "value": "0.12345e0"}. We do
14
+ # want to keep floating point numbers serialized as floating point numbers, even at the expense of loosing a little
15
+ # bit of precision during the conversion. So, in the above example, the correct serialization would be:
16
+ # { "value": 0.12345}
17
+ def to_json(options = nil) #:nodoc:
18
+ if finite?
19
+ self.to_f.to_s
20
+ else
21
+ 'null'
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,14 @@
1
+ class Error
2
+ class BadResponseCodeError < StandardError
3
+ attr_reader :url, :response_code
4
+
5
+ def initialize(response_code, url)
6
+ @response_code = response_code
7
+ @url = url
8
+ end
9
+
10
+ def message
11
+ "Got response code '#{@response_code}' contacting NewRelic at URL '#{@url}'"
12
+ end
13
+ end
14
+ end
@@ -6,17 +6,23 @@ require 'uri'
6
6
  require 'zlib'
7
7
  require 'json'
8
8
  require 'java'
9
+ require 'set'
10
+ require_relative './config/bigdecimal_patch'
11
+ require_relative './exception/error'
9
12
 
10
13
  class LogStash::Outputs::NewRelic < LogStash::Outputs::Base
11
14
  java_import java.util.concurrent.Executors;
12
15
  java_import java.util.concurrent.Semaphore;
13
16
 
17
+ NON_RETRYABLE_CODES = Set[401, 403]
18
+
14
19
  config_name "newrelic"
15
20
 
16
21
  config :api_key, :validate => :password, :required => false
17
22
  config :license_key, :validate => :password, :required => false
18
23
  config :concurrent_requests, :validate => :number, :default => 1
19
24
  config :base_uri, :validate => :string, :default => "https://log-api.newrelic.com/log/v1"
25
+ config :max_retries, :validate => :number, :default => 3
20
26
 
21
27
  public
22
28
 
@@ -26,12 +32,12 @@ class LogStash::Outputs::NewRelic < LogStash::Outputs::Base
26
32
  raise LogStash::ConfigurationError, "Must provide a license key or api key", caller
27
33
  end
28
34
  auth = {
29
- @api_key.nil? ? 'X-License-Key': 'X-Insert-Key' =>
30
- @api_key.nil? ? @license_key.value : @api_key.value
35
+ @api_key.nil? ? 'X-License-Key' : 'X-Insert-Key' =>
36
+ @api_key.nil? ? @license_key.value : @api_key.value
31
37
  }
32
38
  @header = {
33
- 'X-Event-Source' => 'logs',
34
- 'Content-Encoding' => 'gzip'
39
+ 'X-Event-Source' => 'logs',
40
+ 'Content-Encoding' => 'gzip'
35
41
  }.merge(auth).freeze
36
42
  @executor = java.util.concurrent.Executors.newFixedThreadPool(@concurrent_requests)
37
43
  @semaphor = java.util.concurrent.Semaphore.new(@concurrent_requests)
@@ -58,11 +64,10 @@ class LogStash::Outputs::NewRelic < LogStash::Outputs::Base
58
64
  end
59
65
  end
60
66
 
61
-
62
67
  def encode(event_hash)
63
68
  log_message_hash = {
64
- # non-intrinsic attributes get put into 'attributes'
65
- :attributes => event_hash
69
+ # non-intrinsic attributes get put into 'attributes'
70
+ :attributes => event_hash
66
71
  }
67
72
 
68
73
  # intrinsic attributes go at the top level
@@ -88,15 +93,15 @@ class LogStash::Outputs::NewRelic < LogStash::Outputs::Base
88
93
  payload.push(encode(event.to_hash))
89
94
  end
90
95
  payload = {
91
- :common => {
92
- :attributes => {
93
- :plugin => {
94
- :type => 'logstash',
95
- :version => LogStash::Outputs::NewRelicVersion::VERSION,
96
- }
97
- }
98
- },
99
- :logs => payload
96
+ :common => {
97
+ :attributes => {
98
+ :plugin => {
99
+ :type => 'logstash',
100
+ :version => LogStash::Outputs::NewRelicVersion::VERSION,
101
+ }
102
+ }
103
+ },
104
+ :logs => payload
100
105
  }
101
106
  @semaphor.acquire()
102
107
  execute = @executor.java_method :submit, [java.lang.Runnable]
@@ -115,17 +120,50 @@ class LogStash::Outputs::NewRelic < LogStash::Outputs::Base
115
120
 
116
121
  def handle_response(response)
117
122
  if !(200 <= response.code.to_i && response.code.to_i < 300)
118
- @logger.error("Request returned " + response.code + " " + response.body)
123
+ raise Error::BadResponseCodeError.new(response.code.to_i, @base_uri)
119
124
  end
120
125
  end
121
126
 
122
127
  def nr_send(payload)
123
- http = Net::HTTP.new(@end_point.host, 443)
124
- request = Net::HTTP::Post.new(@end_point.request_uri)
125
- http.use_ssl = true
126
- http.verify_mode = OpenSSL::SSL::VERIFY_PEER
127
- @header.each {|k, v| request[k] = v}
128
- request.body = payload
129
- handle_response(http.request(request))
128
+ retries = 0
129
+ begin
130
+ http = Net::HTTP.new(@end_point.host, 443)
131
+ request = Net::HTTP::Post.new(@end_point.request_uri)
132
+ http.use_ssl = true
133
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
134
+ @header.each { |k, v| request[k] = v }
135
+ request.body = payload
136
+ handle_response(http.request(request))
137
+ rescue Error::BadResponseCodeError => e
138
+ @logger.error(e.message)
139
+ if (should_retry(retries) && is_retryable_code(e))
140
+ retries += 1
141
+ sleep(1)
142
+ retry
143
+ end
144
+ rescue => e
145
+ # Stuff that should never happen
146
+ # For all other errors print out full issues
147
+ @logger.error(
148
+ "An unknown error occurred sending a bulk request to NewRelic.",
149
+ :error_message => e.message,
150
+ :error_class => e.class.name,
151
+ :backtrace => e.backtrace
152
+ )
153
+ if (should_retry(retries))
154
+ retries += 1
155
+ sleep(1)
156
+ retry
157
+ end
158
+ end
159
+ end
160
+
161
+ def should_retry(retries)
162
+ retries < @max_retries
163
+ end
164
+
165
+ def is_retryable_code(response_error)
166
+ error_code = response_error.response_code
167
+ !NON_RETRYABLE_CODES.include?(error_code)
130
168
  end
131
169
  end # class LogStash::Outputs::NewRelic
@@ -1,7 +1,7 @@
1
1
  module LogStash
2
2
  module Outputs
3
3
  module NewRelicVersion
4
- VERSION = "1.1.1"
4
+ VERSION = "1.2.1"
5
5
  end
6
6
  end
7
7
  end
@@ -7,7 +7,7 @@ Gem::Specification.new do |s|
7
7
  s.name = 'logstash-output-newrelic'
8
8
  s.version = LogStash::Outputs::NewRelicVersion::VERSION
9
9
  s.licenses = ['Apache-2.0']
10
- s.summary = "Sends Lostash events to New Relic"
10
+ s.summary = "Sends Logstash events to New Relic"
11
11
  s.homepage = 'https://github.com/newrelic/logstash-output-plugin'
12
12
  s.authors = ['New Relic Logging Team']
13
13
  s.email = 'logging-team@newrelic.com'
@@ -26,6 +26,7 @@ Gem::Specification.new do |s|
26
26
  s.add_runtime_dependency "logstash-codec-plain"
27
27
  s.add_development_dependency "logstash-devutils"
28
28
  s.add_development_dependency "webmock"
29
+ s.add_development_dependency "rspec"
29
30
  s.add_development_dependency "rspec-wait"
30
-
31
+ s.add_development_dependency "rspec_junit_formatter"
31
32
  end
@@ -21,7 +21,6 @@ describe LogStash::Outputs::NewRelic do
21
21
  }
22
22
  }
23
23
 
24
-
25
24
  before(:each) do
26
25
  @newrelic_output = LogStash::Plugin.lookup("output", "newrelic").new(simple_config)
27
26
  @newrelic_output.register
@@ -32,6 +31,7 @@ describe LogStash::Outputs::NewRelic do
32
31
  @newrelic_output.shutdown
33
32
  end
34
33
  end
34
+
35
35
  context "license key tests" do
36
36
  it "sets license key when given in the header" do
37
37
  stub_request(:any, base_uri).to_return(status: 200)
@@ -279,5 +279,104 @@ describe LogStash::Outputs::NewRelic do
279
279
  .with { |request| single_gzipped_message(request.body)['message'] == 'Test message 2' })
280
280
  .to have_been_made
281
281
  end
282
+
283
+ it "retry when receive retryable http error code" do
284
+ stub_request(:any, base_uri)
285
+ .to_return(status: 500)
286
+ .to_return(status: 200)
287
+
288
+ event1 = LogStash::Event.new({ "message" => "Test message 1" })
289
+ @newrelic_output.multi_receive([event1])
290
+
291
+ wait_for(a_request(:post, base_uri)
292
+ .with { |request| single_gzipped_message(request.body)['message'] == 'Test message 1' })
293
+ .to have_been_made.times(2)
294
+ end
295
+
296
+ it "not retry when receive a non retryable http error code" do
297
+ stub_request(:any, base_uri)
298
+ .to_return(status: 401)
299
+
300
+ event1 = LogStash::Event.new({ "message" => "Test message 1" })
301
+ @newrelic_output.multi_receive([event1])
302
+ # Due the async behavior we need to wait to be sure that the method was not called more than 1 time
303
+ sleep(2)
304
+ wait_for(a_request(:post, base_uri)
305
+ .with { |request| single_gzipped_message(request.body)['message'] == 'Test message 1' })
306
+ .to have_been_made.times(1)
307
+ end
308
+
309
+ it "not retries when retry is disabled" do
310
+ @newrelic_output = LogStash::Plugin.lookup("output", "newrelic").new(
311
+ { "base_uri" => base_uri, "license_key" => api_key, "max_retries" => '0' }
312
+ )
313
+ @newrelic_output.register
314
+ stub_request(:any, base_uri)
315
+ .to_return(status: 500)
316
+
317
+ event1 = LogStash::Event.new({ "message" => "Test message 1" })
318
+ @newrelic_output.multi_receive([event1])
319
+ # Due the async behavior we need to wait to be sure that the method was not called more than 1 time
320
+ sleep(2)
321
+ wait_for(a_request(:post, base_uri)
322
+ .with { |request| single_gzipped_message(request.body)['message'] == 'Test message 1' })
323
+ .to have_been_made.times(1)
324
+ end
325
+
326
+ it "retry when receive a not expected exception" do
327
+ stub_request(:any, base_uri)
328
+ .to_raise(StandardError.new("from test"))
329
+ .to_return(status: 200)
330
+
331
+ event1 = LogStash::Event.new({ "message" => "Test message 1" })
332
+ @newrelic_output.multi_receive([event1])
333
+ wait_for(a_request(:post, base_uri)
334
+ .with { |request| single_gzipped_message(request.body)['message'] == 'Test message 1' })
335
+ .to have_been_made.times(2)
336
+ end
337
+ end
338
+
339
+ context "JSON serialization" do
340
+ it "serializes floating point numbers as floating point numbers" do
341
+ stub_request(:any, base_uri).to_return(status: 200)
342
+
343
+ event = LogStash::Event.new({ "floatingpoint" => 0.12345 })
344
+ @newrelic_output.multi_receive([event])
345
+
346
+ wait_for(a_request(:post, base_uri)
347
+ .with { |request|
348
+ message = single_gzipped_message(request.body)
349
+ message['attributes']['floatingpoint'] == 0.12345
350
+ }
351
+ ).to have_been_made
352
+ end
353
+
354
+ it "serializes BigDecimals as floating point numbers" do
355
+ stub_request(:any, base_uri).to_return(status: 200)
356
+
357
+ event = LogStash::Event.new({ "bigdecimal" => BigDecimal('0.12345') })
358
+ @newrelic_output.multi_receive([event])
359
+
360
+ wait_for(a_request(:post, base_uri)
361
+ .with { |request|
362
+ message = single_gzipped_message(request.body)
363
+ message['attributes']['bigdecimal'] == 0.12345
364
+ }
365
+ ).to have_been_made
366
+ end
367
+
368
+ it "serializes NaN as null" do
369
+ stub_request(:any, base_uri).to_return(status: 200)
370
+
371
+ event = LogStash::Event.new({ "nan" => BigDecimal('NaN') })
372
+ @newrelic_output.multi_receive([event])
373
+
374
+ wait_for(a_request(:post, base_uri)
375
+ .with { |request|
376
+ message = single_gzipped_message(request.body)
377
+ message['attributes']['nan'] == nil
378
+ }
379
+ ).to have_been_made
380
+ end
282
381
  end
283
382
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-output-newrelic
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - New Relic Logging Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-09-04 00:00:00.000000000 Z
11
+ date: 2021-04-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -17,8 +17,8 @@ dependencies:
17
17
  - !ruby/object:Gem::Version
18
18
  version: '2.0'
19
19
  name: logstash-core-plugin-api
20
- prerelease: false
21
20
  type: :runtime
21
+ prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
@@ -31,8 +31,8 @@ dependencies:
31
31
  - !ruby/object:Gem::Version
32
32
  version: '0'
33
33
  name: logstash-codec-plain
34
- prerelease: false
35
34
  type: :runtime
35
+ prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
@@ -45,8 +45,8 @@ dependencies:
45
45
  - !ruby/object:Gem::Version
46
46
  version: '0'
47
47
  name: logstash-devutils
48
- prerelease: false
49
48
  type: :development
49
+ prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - ">="
@@ -59,8 +59,22 @@ dependencies:
59
59
  - !ruby/object:Gem::Version
60
60
  version: '0'
61
61
  name: webmock
62
+ type: :development
62
63
  prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ name: rspec
63
76
  type: :development
77
+ prerelease: false
64
78
  version_requirements: !ruby/object:Gem::Requirement
65
79
  requirements:
66
80
  - - ">="
@@ -73,8 +87,22 @@ dependencies:
73
87
  - !ruby/object:Gem::Version
74
88
  version: '0'
75
89
  name: rspec-wait
90
+ type: :development
76
91
  prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ name: rspec_junit_formatter
77
104
  type: :development
105
+ prerelease: false
78
106
  version_requirements: !ruby/object:Gem::Requirement
79
107
  requirements:
80
108
  - - ">="
@@ -92,6 +120,8 @@ files:
92
120
  - Gemfile
93
121
  - LICENSE
94
122
  - README.md
123
+ - lib/logstash/outputs/config/bigdecimal_patch.rb
124
+ - lib/logstash/outputs/exception/error.rb
95
125
  - lib/logstash/outputs/newrelic.rb
96
126
  - lib/logstash/outputs/newrelic_version/version.rb
97
127
  - logstash-output-newrelic.gemspec
@@ -117,10 +147,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
117
147
  - !ruby/object:Gem::Version
118
148
  version: '0'
119
149
  requirements: []
120
- rubyforge_project:
121
- rubygems_version: 2.7.6
150
+ rubygems_version: 3.0.6
122
151
  signing_key:
123
152
  specification_version: 4
124
- summary: Sends Lostash events to New Relic
153
+ summary: Sends Logstash events to New Relic
125
154
  test_files:
126
155
  - spec/outputs/newrelic_spec.rb