newrelic_plugin 1.0.3 → 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.
- data/.gitignore +1 -0
- data/CHANGELOG.md +44 -1
- data/README.md +11 -5
- data/lib/newrelic_platform_binding.rb +11 -0
- data/lib/newrelic_platform_binding/component.rb +27 -0
- data/lib/newrelic_platform_binding/config.rb +39 -0
- data/lib/newrelic_platform_binding/connection.rb +86 -0
- data/lib/newrelic_platform_binding/context.rb +49 -0
- data/lib/newrelic_platform_binding/logger.rb +40 -0
- data/lib/newrelic_platform_binding/metric.rb +49 -0
- data/lib/newrelic_platform_binding/request.rb +111 -0
- data/lib/newrelic_plugin.rb +1 -4
- data/lib/newrelic_plugin/agent.rb +14 -21
- data/lib/newrelic_plugin/config.rb +2 -1
- data/lib/newrelic_plugin/processors/rate_processor.rb +3 -3
- data/lib/newrelic_plugin/run.rb +97 -54
- data/lib/newrelic_plugin/setup.rb +10 -19
- data/lib/newrelic_plugin/version.rb +1 -1
- data/newrelic_plugin.gemspec +4 -4
- data/test/newrelic_platform_binding/component_test.rb +16 -0
- data/test/newrelic_platform_binding/config_test.rb +89 -0
- data/test/newrelic_platform_binding/connection_test.rb +67 -0
- data/test/newrelic_platform_binding/context_test.rb +35 -0
- data/test/newrelic_platform_binding/logger_test.rb +33 -0
- data/test/newrelic_platform_binding/metric_test.rb +97 -0
- data/test/newrelic_platform_binding/request_test.rb +127 -0
- data/test/newrelic_plugin/agent_test.rb +127 -0
- data/test/newrelic_plugin/run_test.rb +77 -0
- data/test/newrelic_plugin/setup_test.rb +17 -0
- data/test/test_helper.rb +11 -4
- metadata +59 -17
- data/lib/newrelic_plugin/data_collector.rb +0 -67
- data/lib/newrelic_plugin/logger.rb +0 -19
- data/lib/newrelic_plugin/new_relic_connection.rb +0 -67
- data/lib/newrelic_plugin/new_relic_message.rb +0 -173
- data/test/agent_test.rb +0 -153
- data/test/logger_test.rb +0 -21
- data/test/manual_test.rb +0 -20
- data/test/new_relic_message_test.rb +0 -76
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -4,7 +4,50 @@
|
|
4
4
|
|
5
5
|
**Features**
|
6
6
|
|
7
|
-
|
7
|
+
|
8
|
+
### v1.2.1 - September 10, 2013 ###
|
9
|
+
|
10
|
+
**Bug Fixes**
|
11
|
+
|
12
|
+
* Send agent version to the HTTP API, not the SDK version.
|
13
|
+
* Stop using SSL by default in Ruby versions below 1.9 (this fixes an issue where the agent stops reporting)
|
14
|
+
* Set timeouts on HTTP API connection (fixes an issue where the agent stops reporting in Ruby 1.9 and higher)
|
15
|
+
|
16
|
+
|
17
|
+
### v1.2.0 - August 19, 2013 ###
|
18
|
+
|
19
|
+
**Features**
|
20
|
+
|
21
|
+
* Aggregate data when the collector is unreachable.
|
22
|
+
|
23
|
+
**Bug Fixes**
|
24
|
+
|
25
|
+
* Fixed issue where the ssl_host_verification flag was not working.
|
26
|
+
* Fixed ordering of min and max in metric array that is sent to the HTTP API.
|
27
|
+
|
28
|
+
|
29
|
+
### v1.1.1 - August 13, 2013 ###
|
30
|
+
|
31
|
+
**Bug Fixes**
|
32
|
+
|
33
|
+
* Fixed issue where to_set method was not found when requiring this gem without using bundler.
|
34
|
+
* Added JSON as a dependency to provide Ruby 1.8.7 support.
|
35
|
+
|
36
|
+
### v1.1.0 - August 5, 2013 ###
|
37
|
+
|
38
|
+
**Features**
|
39
|
+
|
40
|
+
* Improved logging
|
41
|
+
* Support for proxies
|
42
|
+
|
43
|
+
**Bug Fixes**
|
44
|
+
|
45
|
+
* Duration of data collection time is now calculated to match actual duration
|
46
|
+
|
47
|
+
**Changes**
|
48
|
+
|
49
|
+
* Dropped dependency on Faraday
|
50
|
+
|
8
51
|
### v1.0.3 - June 25, 2013 ###
|
9
52
|
|
10
53
|
**Features**
|
data/README.md
CHANGED
@@ -2,9 +2,16 @@
|
|
2
2
|
|
3
3
|
## Requirements
|
4
4
|
|
5
|
-
* Tested with Ruby 1.8.7
|
6
|
-
|
7
|
-
|
5
|
+
* Tested with Ruby 1.8.7, 1.9.3, 2.0.0
|
6
|
+
|
7
|
+
Note: In Ruby 1.8.7 SSL is disabled by default due to issues with how Net::HTTP handles connection timeouts.
|
8
|
+
If you override this the plugin may occasionally stop reporting data and require a restart.
|
9
|
+
To override you can add the following to `newrelic:` section of the newrelic_config.yml.
|
10
|
+
|
11
|
+
```
|
12
|
+
endpoint: 'https://platform-api.newrelic.com'
|
13
|
+
```
|
14
|
+
|
8
15
|
|
9
16
|
## Get Started
|
10
17
|
|
@@ -36,8 +43,7 @@ Reach out to us at
|
|
36
43
|
There you'll find documentation, FAQs, and forums where you can submit
|
37
44
|
suggestions and discuss with staff and other users.
|
38
45
|
|
39
|
-
Also available is community support on
|
40
|
-
on irc.freenode.net
|
46
|
+
Also available is [community support on Stack Overflow](http://stackoverflow.com/questions/tagged/newrelic-platform).
|
41
47
|
|
42
48
|
Find a bug? E-mail <support@newrelic.com>, or submit a ticket to
|
43
49
|
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'newrelic_platform_binding/logger'
|
2
|
+
require 'newrelic_platform_binding/config'
|
3
|
+
require 'newrelic_platform_binding/request'
|
4
|
+
require 'newrelic_platform_binding/context'
|
5
|
+
require 'newrelic_platform_binding/component'
|
6
|
+
require 'newrelic_platform_binding/metric'
|
7
|
+
require 'newrelic_platform_binding/connection'
|
8
|
+
|
9
|
+
################################################################################
|
10
|
+
# This is a Provisional API which is subject to change.
|
11
|
+
################################################################################
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module NewRelic
|
2
|
+
module Binding
|
3
|
+
class Component
|
4
|
+
attr_reader :name, :guid
|
5
|
+
attr_accessor :last_delivered_at
|
6
|
+
|
7
|
+
def initialize(name, guid)
|
8
|
+
@name = name
|
9
|
+
@guid = guid
|
10
|
+
@last_delivered_at = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def key
|
14
|
+
return (name + guid)
|
15
|
+
end
|
16
|
+
|
17
|
+
def duration
|
18
|
+
if last_delivered_at.nil?
|
19
|
+
return NewRelic::Binding::Config.poll_cycle_period
|
20
|
+
else
|
21
|
+
return (Time.now - last_delivered_at).ceil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module NewRelic
|
2
|
+
module Binding
|
3
|
+
class Config
|
4
|
+
def self.endpoint=(url)
|
5
|
+
@endpoint = url
|
6
|
+
if self.use_ssl? and !self.ssl_supported?
|
7
|
+
Logger.warn('Using SSL is not recommended when using Ruby versions below 1.9')
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.use_ssl?
|
12
|
+
@endpoint.start_with?('https')
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.ssl_supported?
|
16
|
+
!(!defined?(RUBY_ENGINE) || (RUBY_ENGINE == 'ruby' && RUBY_VERSION < '1.9.0'))
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.skip_ssl_host_verification?
|
20
|
+
!@ssl_host_verification
|
21
|
+
end
|
22
|
+
|
23
|
+
if self.ssl_supported?
|
24
|
+
@endpoint = 'https://platform-api.newrelic.com'
|
25
|
+
else
|
26
|
+
@endpoint = 'http://platform-api.newrelic.com'
|
27
|
+
Logger.warn('SSL is disabled by default when using Ruby 1.8.x')
|
28
|
+
end
|
29
|
+
@uri = '/platform/v1/metrics'
|
30
|
+
@ssl_host_verification = true
|
31
|
+
@poll_cycle_period = 60
|
32
|
+
@proxy = nil
|
33
|
+
class << self
|
34
|
+
attr_accessor :ssl_host_verification, :uri, :poll_cycle_period, :proxy
|
35
|
+
attr_reader :endpoint
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'uri'
|
3
|
+
require 'net/http'
|
4
|
+
require 'net/https'
|
5
|
+
|
6
|
+
module NewRelic
|
7
|
+
module Binding
|
8
|
+
class Connection
|
9
|
+
attr_reader :license_key, :url
|
10
|
+
|
11
|
+
def initialize(context)
|
12
|
+
@url = Config.endpoint + Config.uri
|
13
|
+
@license_key = context.license_key
|
14
|
+
end
|
15
|
+
|
16
|
+
def send_request(data)
|
17
|
+
begin
|
18
|
+
Logger.debug("JSON payload: #{data}")
|
19
|
+
uri = URI.parse(url)
|
20
|
+
if Config.proxy.nil?
|
21
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
22
|
+
else
|
23
|
+
proxy = Config.proxy
|
24
|
+
http = Net::HTTP.new(uri.host, uri.port, proxy['address'], proxy['port'], proxy['user'], proxy['password'])
|
25
|
+
end
|
26
|
+
if Config.use_ssl?
|
27
|
+
http.use_ssl = true
|
28
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE if Config.skip_ssl_host_verification?
|
29
|
+
end
|
30
|
+
http.open_timeout = 20
|
31
|
+
http.read_timeout = 20
|
32
|
+
request = Net::HTTP::Post.new(uri.path)
|
33
|
+
request['content-type'] = 'application/json'
|
34
|
+
request['X-License-Key'] = @license_key
|
35
|
+
request.body = data
|
36
|
+
response = http.request(request)
|
37
|
+
return evaluate_response(response)
|
38
|
+
rescue Timeout::Error => err
|
39
|
+
Logger.warn "Connection Timeout Error: #{err.inspect} #{err.message}"
|
40
|
+
return false
|
41
|
+
rescue => err
|
42
|
+
Logger.warn "HTTP Connection Error: #{err.inspect} #{err.message}"
|
43
|
+
return false
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
def evaluate_response(response)
|
49
|
+
return_status = nil
|
50
|
+
begin
|
51
|
+
if response.nil?
|
52
|
+
last_result = { "error" => "no response" }
|
53
|
+
return_status = "FAILED: No response"
|
54
|
+
elsif response && response.code == '200'
|
55
|
+
last_result = JSON.parse(response.body)
|
56
|
+
if last_result["status"] != "ok"
|
57
|
+
return_status = "FAILED[#{response.code}] <#{url}>: #{last_result["error"]}"
|
58
|
+
end
|
59
|
+
elsif response && response.code == '403' && response.body == "DISABLE_NEW_RELIC"
|
60
|
+
Logger.fatal "Agent has been disabled remotely by New Relic"
|
61
|
+
abort "Agent has been disabled remotely by New Relic"
|
62
|
+
else
|
63
|
+
if response.body.size > 0
|
64
|
+
last_result = JSON.parse(response.body)
|
65
|
+
else
|
66
|
+
last_result = {"error" => "no data returned"}
|
67
|
+
end
|
68
|
+
return_status = "FAILED[#{response.code}] <#{url}>: #{last_result["error"]}"
|
69
|
+
end
|
70
|
+
rescue => err
|
71
|
+
return_status = "FAILED[#{response.code}] <#{url}>: Could not parse response: #{err.message}"
|
72
|
+
end
|
73
|
+
if return_status
|
74
|
+
if response and response.code == '503'
|
75
|
+
Logger.warn("Collector temporarily unavailable. Continuing.")
|
76
|
+
else
|
77
|
+
Logger.error("#{return_status}")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
return_status.nil?
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module NewRelic
|
2
|
+
module Binding
|
3
|
+
class Context
|
4
|
+
AGGREGATION_LIMIT = 20
|
5
|
+
attr_reader :components, :license_key
|
6
|
+
attr_accessor :version, :host, :pid
|
7
|
+
|
8
|
+
def initialize(license_key)
|
9
|
+
@version = nil
|
10
|
+
@host = nil
|
11
|
+
@request = Request.new(self)
|
12
|
+
@pid = nil
|
13
|
+
@license_key = license_key
|
14
|
+
@components = []
|
15
|
+
@aggregation_start = Time.now
|
16
|
+
end
|
17
|
+
|
18
|
+
def create_component(name, guid)
|
19
|
+
component = Component.new(name, guid)
|
20
|
+
@components.push(component)
|
21
|
+
return component
|
22
|
+
end
|
23
|
+
|
24
|
+
def get_request()
|
25
|
+
if past_aggregation_limit?
|
26
|
+
@components.each do |component|
|
27
|
+
component.last_delivered_at = nil
|
28
|
+
end
|
29
|
+
@request = Request.new(self)
|
30
|
+
elsif @request.delivered?
|
31
|
+
@request = Request.new(self)
|
32
|
+
else
|
33
|
+
@request
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def last_request_delivered_at=(delivered_at)
|
38
|
+
@aggregation_start = delivered_at
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
def past_aggregation_limit?
|
43
|
+
@aggregation_start < Time.now - AGGREGATION_LIMIT * 60
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module NewRelic
|
2
|
+
require 'time'
|
3
|
+
require 'logger'
|
4
|
+
|
5
|
+
class Logger
|
6
|
+
@log = ::Logger.new(STDOUT)
|
7
|
+
@log.level = ::Logger::WARN
|
8
|
+
@log.formatter = proc { |severity, datetime, progname, msg| "[#{Time.iso8601(Time.now.utc.iso8601).to_s}] #{severity}: #{msg}\n" }
|
9
|
+
class << self
|
10
|
+
def log_level=(level)
|
11
|
+
@log.level = level
|
12
|
+
end
|
13
|
+
|
14
|
+
def fatal(message)
|
15
|
+
@log.fatal(message)
|
16
|
+
end
|
17
|
+
|
18
|
+
def error(message)
|
19
|
+
@log.error(message)
|
20
|
+
end
|
21
|
+
|
22
|
+
def warn(message)
|
23
|
+
@log.warn(message)
|
24
|
+
end
|
25
|
+
|
26
|
+
def info(message)
|
27
|
+
@log.info(message)
|
28
|
+
end
|
29
|
+
|
30
|
+
def debug(message)
|
31
|
+
@log.debug(message)
|
32
|
+
end
|
33
|
+
|
34
|
+
def log_metrics=(value)
|
35
|
+
@log_metrics = value
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'set'
|
2
|
+
module NewRelic
|
3
|
+
module Binding
|
4
|
+
class Metric
|
5
|
+
attr_reader :component, :name, :value, :count, :min, :max, :sum_of_squares
|
6
|
+
|
7
|
+
def initialize(component, name, input_value, options = {} )
|
8
|
+
value = input_value.to_f
|
9
|
+
@component = component
|
10
|
+
@name = name
|
11
|
+
@value = value
|
12
|
+
if options_has_required_keys(options)
|
13
|
+
@count = options[:count].to_i
|
14
|
+
@min = options[:min].to_f
|
15
|
+
@max = options[:max].to_f
|
16
|
+
@sum_of_squares = options[:sum_of_squares].to_f
|
17
|
+
else
|
18
|
+
Logger.warn("Metric #{@name} count, min, max, and sum_of_squares are all required if one is set, falling back to value only") unless options.size == 0
|
19
|
+
@count = 1
|
20
|
+
@min = value
|
21
|
+
@max = value
|
22
|
+
@sum_of_squares = (value * value)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def aggregate(metric)
|
27
|
+
@value += metric.value
|
28
|
+
@count += metric.count
|
29
|
+
@min = [@min, metric.min].min
|
30
|
+
@max = [@max, metric.max].max
|
31
|
+
@sum_of_squares += metric.sum_of_squares
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_hash
|
35
|
+
{
|
36
|
+
name => [
|
37
|
+
@value, @count, @min, @max, @sum_of_squares
|
38
|
+
]
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def options_has_required_keys(options)
|
45
|
+
options.keys.to_set.superset?(Set.new([:count, :min, :max, :sum_of_squares]))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'json'
|
2
|
+
module NewRelic
|
3
|
+
module Binding
|
4
|
+
class Request
|
5
|
+
attr_reader :context
|
6
|
+
|
7
|
+
def initialize(context)
|
8
|
+
@context = context
|
9
|
+
@duration = nil
|
10
|
+
@metrics = {}
|
11
|
+
@delivered = false
|
12
|
+
return self
|
13
|
+
end
|
14
|
+
|
15
|
+
def deliver
|
16
|
+
metrics_hash = build_request_data_structure
|
17
|
+
connection = Connection.new(@context)
|
18
|
+
if connection.send_request(metrics_hash.to_json)
|
19
|
+
@delivered = true
|
20
|
+
delivered_at = Time.now
|
21
|
+
context.last_request_delivered_at = delivered_at
|
22
|
+
duration_warning = false
|
23
|
+
@context.components.each do |component|
|
24
|
+
if @metrics.has_key?(component.key)
|
25
|
+
duration_warning = true if component.duration > 600
|
26
|
+
component.last_delivered_at = delivered_at
|
27
|
+
end
|
28
|
+
end
|
29
|
+
Logger.warn("Duration of more than 10 minutes between sending data to New Relic, this will cause plugins to show up as not reporting") if duration_warning
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def add_metric(component, name, value, options = {})
|
34
|
+
metric = find_metric(component, name)
|
35
|
+
new_metric = Metric.new(self, name, value, options)
|
36
|
+
if metric.nil?
|
37
|
+
metric = new_metric
|
38
|
+
@metrics[component.key] ||= []
|
39
|
+
@metrics[component.key].push(metric)
|
40
|
+
else
|
41
|
+
metric.aggregate(new_metric)
|
42
|
+
end
|
43
|
+
return metric
|
44
|
+
end
|
45
|
+
|
46
|
+
def metric_count
|
47
|
+
count = 0
|
48
|
+
@metrics.each do |m|
|
49
|
+
count = count + m.size
|
50
|
+
end
|
51
|
+
count
|
52
|
+
end
|
53
|
+
|
54
|
+
def component_count
|
55
|
+
@metrics.size
|
56
|
+
end
|
57
|
+
|
58
|
+
def delivered?
|
59
|
+
@delivered
|
60
|
+
end
|
61
|
+
private
|
62
|
+
def find_metric(component, name)
|
63
|
+
@metrics[component.key].find { |m| m.name == name } unless @metrics[component.key].nil?
|
64
|
+
end
|
65
|
+
|
66
|
+
def build_request_data_structure
|
67
|
+
{
|
68
|
+
'agent' => build_agent_hash(),
|
69
|
+
'components' => build_components_array()
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
def build_agent_hash
|
74
|
+
agent_hash = {
|
75
|
+
'version' => @context.version,
|
76
|
+
}
|
77
|
+
agent_hash['host'] = @context.host unless @context.host.nil?
|
78
|
+
agent_hash['pid'] = @context.pid unless @context.pid.nil?
|
79
|
+
agent_hash
|
80
|
+
end
|
81
|
+
|
82
|
+
def build_components_array
|
83
|
+
components_array = []
|
84
|
+
@context.components.each do |component|
|
85
|
+
component_hash = {
|
86
|
+
'name' => component.name,
|
87
|
+
'guid' => component.guid,
|
88
|
+
'duration' => component.duration,
|
89
|
+
'metrics' => build_metrics_hash(component)
|
90
|
+
}
|
91
|
+
components_array.push(component_hash)
|
92
|
+
end
|
93
|
+
return components_array
|
94
|
+
end
|
95
|
+
|
96
|
+
def build_metrics_hash(component)
|
97
|
+
metrics_hash = {}
|
98
|
+
if @metrics.has_key?(component.key)
|
99
|
+
@metrics[component.key].each do |metric|
|
100
|
+
metrics_hash.merge!(metric.to_hash)
|
101
|
+
end
|
102
|
+
else
|
103
|
+
Logger.warn("Component with name \"#{component.name}\" and guid \"#{component.guid}\" had no metrics")
|
104
|
+
end
|
105
|
+
metrics_hash
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|