newrelic_plugin 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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