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.
@@ -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,18 @@
1
+ module NewRelic
2
+ module Plugin
3
+ module Processor
4
+ #
5
+ #
6
+ # Processor Base Class
7
+ #
8
+ #
9
+ class Base
10
+ attr_reader :ident,:label
11
+ def initialize ident,label
12
+ @ident=ident
13
+ @label=label
14
+ end
15
+ end
16
+ end
17
+ end
18
+ 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