newrelic_plugin 1.0.2
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/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.md +46 -0
- data/Rakefile +19 -0
- data/lib/newrelic_plugin.rb +12 -0
- data/lib/newrelic_plugin/agent.rb +155 -0
- data/lib/newrelic_plugin/config.rb +84 -0
- data/lib/newrelic_plugin/data_collector.rb +67 -0
- data/lib/newrelic_plugin/error.rb +22 -0
- data/lib/newrelic_plugin/new_relic_connection.rb +67 -0
- data/lib/newrelic_plugin/new_relic_message.rb +173 -0
- data/lib/newrelic_plugin/processor.rb +18 -0
- data/lib/newrelic_plugin/processors/epoch_counter_processor.rb +21 -0
- data/lib/newrelic_plugin/processors/rate_processor.rb +35 -0
- data/lib/newrelic_plugin/run.rb +120 -0
- data/lib/newrelic_plugin/setup.rb +54 -0
- data/lib/newrelic_plugin/simple_syntax.rb +54 -0
- data/lib/newrelic_plugin/version.rb +5 -0
- data/newrelic_plugin.gemspec +47 -0
- data/test/agent_test.rb +153 -0
- data/test/fixtures/valid_payload.json +17 -0
- data/test/manual_test.rb +20 -0
- data/test/new_relic_message_test.rb +76 -0
- data/test/test_helper.rb +15 -0
- metadata +141 -0
@@ -0,0 +1,173 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module NewRelic
|
4
|
+
module Plugin
|
5
|
+
#
|
6
|
+
# NewRelic driver. Provides all methods necessary for accessing the NewRelic service.
|
7
|
+
# Used to store data into NewRelic service.
|
8
|
+
#
|
9
|
+
# Usage:
|
10
|
+
#
|
11
|
+
# nrobj=NewRelic::Plugin::NewRelicConnection.new license_key:"xxx",host:"xxx",port:80,log_http:true,log_metrics:true # log_http,host and port are optional; license key is not.
|
12
|
+
# msg=nrobj.start_message <component_name>,<component_guid>,<component_version>,<duration_in_seconds>
|
13
|
+
# msg.add_stat basename,count_unit,value_unit,count,value,opts={} # opts can include :min, :max, and :sum_of_squares
|
14
|
+
# msg.send_message # Send the list of stats to New Relic
|
15
|
+
#
|
16
|
+
# Example:
|
17
|
+
# nrobj=NewRelic::Plugin::NewRelicConnection.new license_key:"bootstrap_newrelic_admin_license_key_000",host:"localhost",port:8081,log_http:true,log_metrics:true
|
18
|
+
# msg=nrobj.start_message "A Kewl Component","12345678","0.0.1",60
|
19
|
+
# msg.add_stat_fullname "Component/TestMetric1[Bytes/Seconds]",2,34,min:31,max:54,sum_of_squares:1234
|
20
|
+
# msg.add_stat_fullname "Component/AnotherTest[Bytes/Seconds]",2,34,min:31,max:54,sum_of_squares:1234
|
21
|
+
# msg.add_stat_fullname "Component/TestMetric2[Bytes/Seconds]",2,34,min:31,max:54,sum_of_squares:1234
|
22
|
+
# msg.add_stat_fullname "Component/AnotherMetric[Bytes/Seconds]",2,34,min:31,max:54,sum_of_squares:1234
|
23
|
+
# msg.send_metrics # Send the list of stats to New Relic
|
24
|
+
#
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
class NewRelicMessage
|
29
|
+
#
|
30
|
+
#
|
31
|
+
# Create an object to send metrics
|
32
|
+
#
|
33
|
+
#
|
34
|
+
def initialize connection,component_name,component_guid,component_version,duration_in_seconds
|
35
|
+
@connection = connection
|
36
|
+
@component_name = component_name
|
37
|
+
@component_guid = component_guid
|
38
|
+
@component_version = component_version
|
39
|
+
@duration_in_seconds = duration_in_seconds
|
40
|
+
@metrics = [] # Metrics being saved
|
41
|
+
end
|
42
|
+
def add_stat_fullname metric_name,count,value,opts={}
|
43
|
+
entry = {}
|
44
|
+
entry[:metric_name] = metric_name
|
45
|
+
entry[:count] = count
|
46
|
+
entry[:total] = value
|
47
|
+
[:min,:max,:sum_of_squares].each do |key|
|
48
|
+
entry[key] = opts[key]
|
49
|
+
end
|
50
|
+
@metrics << entry
|
51
|
+
end
|
52
|
+
|
53
|
+
def metrics
|
54
|
+
@metrics
|
55
|
+
end
|
56
|
+
|
57
|
+
def send_metrics
|
58
|
+
return_errors = []
|
59
|
+
puts "Metrics for #{@component_name}[#{@component_guid}] for last #{@duration_in_seconds} seconds:" if new_relic_connection.log_metrics?
|
60
|
+
#
|
61
|
+
# Send all metrics in a single transaction
|
62
|
+
#
|
63
|
+
response = deliver_metrics
|
64
|
+
evaluate_response(response)
|
65
|
+
log_send_metrics
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def build_metrics_hash
|
71
|
+
metrics_hash = {}
|
72
|
+
metrics.each do |metric|
|
73
|
+
metric_value = []
|
74
|
+
[:total,:count,:max,:min,:sum_of_squares].each do |key|
|
75
|
+
metric_value.push(metric[key]) if metric[key]
|
76
|
+
end
|
77
|
+
metrics_hash[metric[:metric_name]] = metric_value
|
78
|
+
end
|
79
|
+
return metrics_hash
|
80
|
+
end
|
81
|
+
|
82
|
+
def build_request_payload
|
83
|
+
data = {
|
84
|
+
"agent" => {
|
85
|
+
"name" => @component_name,
|
86
|
+
"version" => @component_version,
|
87
|
+
"host" => ""
|
88
|
+
},
|
89
|
+
"components" => [
|
90
|
+
{
|
91
|
+
"name" => @component_name,
|
92
|
+
"guid" => @component_guid,
|
93
|
+
"duration" => @duration_in_seconds,
|
94
|
+
"metrics" => build_metrics_hash
|
95
|
+
}
|
96
|
+
]
|
97
|
+
}
|
98
|
+
return data.to_json
|
99
|
+
end
|
100
|
+
|
101
|
+
def deliver_metrics
|
102
|
+
begin
|
103
|
+
response = new_relic_connection.connect.post do |req|
|
104
|
+
req.url new_relic_connection.uri
|
105
|
+
req.headers['Content-Type'] = 'application/json'
|
106
|
+
req.body = build_request_payload
|
107
|
+
end
|
108
|
+
rescue => err
|
109
|
+
puts "HTTP Connection Error: #{err.inspect} #{err.message}"
|
110
|
+
end
|
111
|
+
|
112
|
+
return response
|
113
|
+
end
|
114
|
+
|
115
|
+
def evaluate_response(response)
|
116
|
+
return_status = nil
|
117
|
+
if response.nil?
|
118
|
+
last_result={"error" => "no response"}
|
119
|
+
return_status = "FAILED: No response"
|
120
|
+
elsif response && response.status == 200
|
121
|
+
last_result = JSON.parse(response.body)
|
122
|
+
if last_result["status"] != "ok"
|
123
|
+
return_status = "FAILED[#{response.status}] <#{new_relic_connection.url}>: #{last_result["error"]}"
|
124
|
+
end
|
125
|
+
elsif response && response.status == 403 && response.body == "DISABLE_NEW_RELIC"
|
126
|
+
puts "Agent has been disabled remotely by New Relic"
|
127
|
+
abort "Agent has been disabled remotely by New Relic"
|
128
|
+
else
|
129
|
+
begin
|
130
|
+
if response.body.size>0
|
131
|
+
last_result = JSON.parse(response.body)
|
132
|
+
else
|
133
|
+
last_result = {"error" => "no data returned"}
|
134
|
+
end
|
135
|
+
return_status = "FAILED[#{response.status}] <#{new_relic_connection.url}>: #{last_result["error"]}"
|
136
|
+
rescue => err
|
137
|
+
if response
|
138
|
+
return_status = "FAILED[#{response.status}] <#{new_relic_connection.url}>: Could not parse response: #{err}"
|
139
|
+
else
|
140
|
+
return_status = "FAILED: #{err}"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
if return_status
|
145
|
+
if response and response.status == 503 and !new_relic_connection.log_metrics?
|
146
|
+
# If logging not enabled, and it's a 503...be less error-ish...
|
147
|
+
puts " Collector temporarily unavailable...continuing"
|
148
|
+
else
|
149
|
+
# Otherwise, in all cases (logging enabled or not) print an error message
|
150
|
+
puts " ****ERROR: #{return_status}"
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def log_send_metrics
|
156
|
+
if new_relic_connection.log_metrics?
|
157
|
+
puts " Sent #{metrics.size} metrics to New Relic [#{new_relic_connection.url}]:"
|
158
|
+
metrics.each do |metric|
|
159
|
+
val_strs = []
|
160
|
+
[:count,:total,:min,:max,:sum_of_squares].each do |key|
|
161
|
+
val_strs << "#{key}: #{metric[key]}" if metric[key]
|
162
|
+
end
|
163
|
+
puts " #{metric[:metric_name]}: #{val_strs.join(', ')}"
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def new_relic_connection
|
169
|
+
@connection
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module NewRelic::Processor
|
2
|
+
class EpochCounter<NewRelic::Plugin::Processor::Base
|
3
|
+
def initialize
|
4
|
+
super :epoch_counter,"Epoch Counter"
|
5
|
+
end
|
6
|
+
def process val
|
7
|
+
val=val.to_f
|
8
|
+
ret=nil
|
9
|
+
curr_time=Time.now
|
10
|
+
if @last_value and @last_time and curr_time>@last_time
|
11
|
+
ret=(val-@last_value)/(curr_time-@last_time).to_f
|
12
|
+
end
|
13
|
+
@last_value=val
|
14
|
+
@last_time=curr_time
|
15
|
+
# This next line is to avoid large negative spikes during epoch reset events...
|
16
|
+
return nil if ret.nil? or ret<0
|
17
|
+
ret
|
18
|
+
end
|
19
|
+
#Component::Setup.install_processor EpochCounter
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module NewRelic::Processor
|
2
|
+
#
|
3
|
+
#
|
4
|
+
# OBSOLESCENCE WARNING!!!
|
5
|
+
#
|
6
|
+
# The "Rate" processor is being obsoleted. Please do not use in any new agent
|
7
|
+
# development.
|
8
|
+
#
|
9
|
+
# If you feel you need this processor, please check out the "Epoch Counter" processor
|
10
|
+
# and see if that will meet your needs. If not, then you can always do this calculation
|
11
|
+
# yourself within your agent.
|
12
|
+
#
|
13
|
+
# This processor will be removed from the code base shortly...
|
14
|
+
#
|
15
|
+
#
|
16
|
+
class Rate<NewRelic::Plugin::Processor::Base
|
17
|
+
def initialize
|
18
|
+
puts "OBSOLESCENCE WARNING: The 'Rate' processor is obsolete and should not be used."
|
19
|
+
puts "OBSOLESCENCE WARNING: It will be completely removed in the near future."
|
20
|
+
super :rate,"Rate"
|
21
|
+
end
|
22
|
+
def process val
|
23
|
+
val=val.to_f
|
24
|
+
ret=nil
|
25
|
+
curr_time=Time.now
|
26
|
+
if @last_value and @last_time and curr_time>@last_time
|
27
|
+
ret=(val-@last_value)/(curr_time-@last_time).to_f
|
28
|
+
end
|
29
|
+
@last_value=val
|
30
|
+
@last_time=curr_time
|
31
|
+
ret
|
32
|
+
end
|
33
|
+
#Component::Setup.install_processor Rate
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module NewRelic
|
2
|
+
module Plugin
|
3
|
+
#
|
4
|
+
# Run class. Provides entry points and polling initiation support.
|
5
|
+
#
|
6
|
+
# Author:: Lee Atchison <lee@newrelic.com>
|
7
|
+
# Copyright:: Copyright (c) 2012 New Relic, Inc.
|
8
|
+
#
|
9
|
+
class Run
|
10
|
+
#
|
11
|
+
# Primary Driver entry point
|
12
|
+
#
|
13
|
+
def self.setup_and_run component_type_filter=nil
|
14
|
+
run=new
|
15
|
+
run.setup_from_config component_type_filter
|
16
|
+
run.setup_no_config_agents
|
17
|
+
run.loop_forever
|
18
|
+
end
|
19
|
+
def initialize
|
20
|
+
@poll_cycle = (NewRelic::Plugin::Config.config.newrelic["poll"] || 60).to_i
|
21
|
+
@poll_cycle = 60 if (@poll_cycle <= 0) or (@poll_cycle >= 600)
|
22
|
+
puts "WARNING: Poll cycle differs from 60 seconds (current is #{@poll_cycle})" if @poll_cycle!=60
|
23
|
+
end
|
24
|
+
def installed_agents
|
25
|
+
if Setup.installed_agents.size==0
|
26
|
+
puts "No agents installed!"
|
27
|
+
raise NoAgents, "No agents installed"
|
28
|
+
end
|
29
|
+
Setup.installed_agents
|
30
|
+
end
|
31
|
+
#def installed_processors
|
32
|
+
# Setup.installed_processors
|
33
|
+
#end
|
34
|
+
def configured_agents
|
35
|
+
agent_setup.agents
|
36
|
+
end
|
37
|
+
def setup_from_config component_type_filter=nil
|
38
|
+
return unless NewRelic::Plugin::Config.config.agents
|
39
|
+
installed_agents.each do |agent_id,installed_agent|
|
40
|
+
next if component_type_filter and agent_id!=component_type_filter
|
41
|
+
config_list=NewRelic::Plugin::Config.config.agents[agent_id.to_s]
|
42
|
+
next unless config_list
|
43
|
+
[config_list].flatten.each do |config|
|
44
|
+
next unless config
|
45
|
+
# Convert keys to symbols...
|
46
|
+
config.keys.each {|key|config[(key.to_sym rescue key) || key] = config.delete(key)}
|
47
|
+
name=config.delete(:name) # Pull out name and remove from hash
|
48
|
+
agent_setup.create_agent agent_id,name,config
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
#
|
53
|
+
# Add an entry for agents that require no configuration (and hence no instances)
|
54
|
+
#
|
55
|
+
def setup_no_config_agents
|
56
|
+
installed_agents.each do |agent_id,installed_agent|
|
57
|
+
unless installed_agent[:agent_class].config_required?
|
58
|
+
agent_setup.create_agent agent_id,installed_agent[:agent_class].label,{}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
def setup &block
|
63
|
+
block.call(agent_setup)
|
64
|
+
end
|
65
|
+
#
|
66
|
+
# Call this method to loop forever. This will delay an appropriate amount until
|
67
|
+
# the next metric pull is needed, then it will loop thru all configured agents
|
68
|
+
# and call each one in turn so it can perform it's appropriate metric pull.
|
69
|
+
#
|
70
|
+
def loop_forever
|
71
|
+
if configured_agents.size==0
|
72
|
+
err_msg = "No agents configured!"
|
73
|
+
err_msg+= " Check the agents portion of your yml file." unless NewRelic::Plugin::Config.config.options.empty?
|
74
|
+
puts err_msg
|
75
|
+
raise NoAgents, err_msg
|
76
|
+
end
|
77
|
+
installed_agents.each do |agent_id,installed_agent|
|
78
|
+
version = installed_agent[:agent_class].version
|
79
|
+
puts "Agent #{installed_agent[:label]} is at version #{version}" if version
|
80
|
+
end
|
81
|
+
configured_agents.each do |agent|
|
82
|
+
agent.startup if agent.respond_to? :startup
|
83
|
+
end
|
84
|
+
@done=false
|
85
|
+
begin
|
86
|
+
while !@done
|
87
|
+
#
|
88
|
+
# Set last run time
|
89
|
+
@last_run_time=Time.now
|
90
|
+
#
|
91
|
+
# Call each agent
|
92
|
+
cnt=0
|
93
|
+
configured_agents.each do |agent|
|
94
|
+
begin
|
95
|
+
cnt+=agent.run @poll_cycle
|
96
|
+
rescue => err
|
97
|
+
puts "Error occurred in poll cycle: #{err}"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
puts "Gathered #{cnt} statistics"
|
101
|
+
#
|
102
|
+
# Delay until next run
|
103
|
+
secs_to_delay=@poll_cycle-(Time.now-@last_run_time)
|
104
|
+
sleep secs_to_delay if secs_to_delay>0
|
105
|
+
end
|
106
|
+
rescue Interrupt =>err
|
107
|
+
puts "Shutting down..."
|
108
|
+
end
|
109
|
+
configured_agents.each do |agent|
|
110
|
+
agent.shutdown if agent.respond_to? :shutdown
|
111
|
+
end
|
112
|
+
puts "Shutdown complete"
|
113
|
+
end
|
114
|
+
#private
|
115
|
+
def agent_setup
|
116
|
+
@agent_setup||=AgentSetup.new
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module NewRelic
|
2
|
+
module Plugin
|
3
|
+
#
|
4
|
+
# Setup support methods.
|
5
|
+
#
|
6
|
+
# Author:: Lee Atchison <lee@newrelic.com>
|
7
|
+
# Copyright:: Copyright (c) 2012 New Relic, Inc.
|
8
|
+
#
|
9
|
+
#
|
10
|
+
# Setup and Register new agent types and new processors
|
11
|
+
#
|
12
|
+
class Setup
|
13
|
+
class << self
|
14
|
+
def install_agent ident,klass
|
15
|
+
@installed_agents||={}
|
16
|
+
@installed_agents[ident] = {
|
17
|
+
:agent_class => klass::Agent,
|
18
|
+
:label => klass::Agent.label,
|
19
|
+
:ident => ident
|
20
|
+
}
|
21
|
+
end
|
22
|
+
#def install_processor klass
|
23
|
+
# @installed_processors||={}
|
24
|
+
# tmp_instance=klass.new
|
25
|
+
# @installed_processors[tmp_instance.ident]={ident: tmp_instance.ident,processor_class: klass,label: tmp_instance.label}
|
26
|
+
#end
|
27
|
+
def installed_agents
|
28
|
+
@installed_agents||{}
|
29
|
+
end
|
30
|
+
#def installed_processors
|
31
|
+
# @installed_processors||{}
|
32
|
+
#end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# Setup and instantiate new agent instances (of agent type previously setup in NewRelic::Plugin::Setup)
|
38
|
+
#
|
39
|
+
class AgentSetup
|
40
|
+
attr_reader :agents
|
41
|
+
def initialize
|
42
|
+
@agents=[]
|
43
|
+
end
|
44
|
+
def create_agent ident,name,options=nil,&block
|
45
|
+
agent_info=Setup.installed_agents[ident]
|
46
|
+
raise UnknownInstalledAgent,"Unrecognized agent '#{ident}'" unless agent_info
|
47
|
+
agent=agent_info[:agent_class].new name,agent_info,options
|
48
|
+
raise CouldNotInitializeAgent unless agent
|
49
|
+
block.call(agent) if block_given?
|
50
|
+
@agents<<agent
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module NewRelic
|
2
|
+
module Plugin
|
3
|
+
#
|
4
|
+
#
|
5
|
+
# SimpleSyntax:
|
6
|
+
#
|
7
|
+
# This is prototype code...the interface is not finalized nor is the implementation
|
8
|
+
# complete. It's proof-of-concept only and subject to change without notice.
|
9
|
+
#
|
10
|
+
#
|
11
|
+
module SimpleSyntax
|
12
|
+
class Agent < NewRelic::Plugin::Agent::Base
|
13
|
+
no_config_required
|
14
|
+
class << self
|
15
|
+
def guid= guid
|
16
|
+
agent_guid guid
|
17
|
+
end
|
18
|
+
def version= version
|
19
|
+
agent_version version
|
20
|
+
end
|
21
|
+
def human_labels label,&block
|
22
|
+
agent_human_labels label,&block
|
23
|
+
end
|
24
|
+
def poll_cycle_proc= block
|
25
|
+
@@poll_cycle_proc = block
|
26
|
+
end
|
27
|
+
end
|
28
|
+
def poll_cycle
|
29
|
+
mod=Module.new
|
30
|
+
mod.send :define_method,:call_poll_cycle,@@poll_cycle_proc
|
31
|
+
self.extend mod
|
32
|
+
self.call_poll_cycle self
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.setup
|
38
|
+
yield SimpleSyntax::Agent
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.agent_config
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.poll_cycle &block
|
45
|
+
SimpleSyntax::Agent.poll_cycle_proc = block
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.run
|
49
|
+
NewRelic::Plugin::Setup.install_agent :simple_syntax,NewRelic::Plugin::SimpleSyntax
|
50
|
+
NewRelic::Plugin::Run.setup_and_run
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|