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
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Gemfile.lock
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2013 New Relic, Inc.
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# New Relic Platform Agent SDK
|
2
|
+
|
3
|
+
## Requirements
|
4
|
+
|
5
|
+
* Tested with Ruby 1.8.7 and 1.9.3
|
6
|
+
* New Relic account on rpm.newrelic.com, with early access enabled
|
7
|
+
(contact cooper@newrelic.com to set up early access)
|
8
|
+
|
9
|
+
## Get Started
|
10
|
+
|
11
|
+
This repo represents the core Ruby gem used to build plugin agents for
|
12
|
+
the New Relic platform. If you are looking to build or use a platform
|
13
|
+
component, please refer to the
|
14
|
+
[getting started documentation](http://newrelic.com/docs/platform/plugin-development).
|
15
|
+
|
16
|
+
Until this gem is published in rubygems.org, you will need to use this
|
17
|
+
gem by adding it to your [bundler](http://gembundler.com/) Gemfile like this:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
gem "newrelic_plugin", :git => "git@github.com:newrelic-platform/newrelic_plugin.git", :branch => "release"
|
21
|
+
```
|
22
|
+
|
23
|
+
Alternatively you can build and install the gem locally:
|
24
|
+
|
25
|
+
```bash
|
26
|
+
git clone git@github.com:newrelic-platform/newrelic_plugin.git
|
27
|
+
cd newrelic_plugin
|
28
|
+
rake build
|
29
|
+
gem install pkg/newrelic_plugin*
|
30
|
+
```
|
31
|
+
|
32
|
+
## Support
|
33
|
+
|
34
|
+
Reach out to us at
|
35
|
+
[support.newrelic.com](http://support.newrelic.com/).
|
36
|
+
There you'll find documentation, FAQs, and forums where you can submit
|
37
|
+
suggestions and discuss with staff and other users.
|
38
|
+
|
39
|
+
Also available is community support on IRC: we generally use #newrelic
|
40
|
+
on irc.freenode.net
|
41
|
+
|
42
|
+
Find a bug? E-mail <support@newrelic.com>, or submit a ticket to
|
43
|
+
|
44
|
+
[support.newrelic.com](http://support.newrelic.com/).
|
45
|
+
|
46
|
+
Thank you!
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'date'
|
4
|
+
require "bundler/gem_tasks"
|
5
|
+
|
6
|
+
#############################################################################
|
7
|
+
#
|
8
|
+
# Standard tasks
|
9
|
+
#
|
10
|
+
#############################################################################
|
11
|
+
|
12
|
+
task :default => :test
|
13
|
+
|
14
|
+
require 'rake/testtask'
|
15
|
+
Rake::TestTask.new(:test) do |test|
|
16
|
+
test.libs << 'lib' << 'test'
|
17
|
+
test.pattern = 'test/**/*_test.rb'
|
18
|
+
test.verbose = true
|
19
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require "newrelic_plugin/version"
|
2
|
+
require "newrelic_plugin/error"
|
3
|
+
require "newrelic_plugin/config"
|
4
|
+
require "newrelic_plugin/new_relic_connection"
|
5
|
+
require "newrelic_plugin/new_relic_message"
|
6
|
+
require "newrelic_plugin/agent"
|
7
|
+
require "newrelic_plugin/data_collector"
|
8
|
+
require "newrelic_plugin/setup" # cleanup needed
|
9
|
+
require "newrelic_plugin/run" # cleanup needed
|
10
|
+
require "newrelic_plugin/processor"
|
11
|
+
require "newrelic_plugin/processors/epoch_counter_processor" # port needed
|
12
|
+
require "newrelic_plugin/processors/rate_processor" # port needed
|
@@ -0,0 +1,155 @@
|
|
1
|
+
module NewRelic
|
2
|
+
module Plugin
|
3
|
+
#
|
4
|
+
# Primary newrelic_plugin agent.
|
5
|
+
#
|
6
|
+
# Author:: Lee Atchison <lee@newrelic.com>
|
7
|
+
# Copyright:: Copyright (c) 2012 New Relic, Inc.
|
8
|
+
#
|
9
|
+
module Agent
|
10
|
+
class Base
|
11
|
+
class << self
|
12
|
+
attr_reader :label,:instance_label_proc,:guid,:version
|
13
|
+
attr_accessor :config_options_list
|
14
|
+
|
15
|
+
|
16
|
+
#
|
17
|
+
# Define the GUID for this agent.
|
18
|
+
#
|
19
|
+
def agent_guid str
|
20
|
+
raise "Did not set GUID" if str.nil? or str==""
|
21
|
+
@guid=str
|
22
|
+
end
|
23
|
+
|
24
|
+
#
|
25
|
+
# Define version for this agent
|
26
|
+
#
|
27
|
+
def agent_version str
|
28
|
+
@version = str
|
29
|
+
end
|
30
|
+
|
31
|
+
#
|
32
|
+
#
|
33
|
+
# Human Readable labels for plugin.
|
34
|
+
# label - Name of the plugin
|
35
|
+
# block return - Name of a particular instance of a plugin component.
|
36
|
+
#
|
37
|
+
#
|
38
|
+
def agent_human_labels label,&block
|
39
|
+
@label=label
|
40
|
+
@instance_label_proc=block
|
41
|
+
end
|
42
|
+
|
43
|
+
#
|
44
|
+
#
|
45
|
+
# Support for single instance, non-configurable agents.
|
46
|
+
#
|
47
|
+
#
|
48
|
+
def no_config_required
|
49
|
+
@no_config_required=true
|
50
|
+
end
|
51
|
+
def config_required?
|
52
|
+
!@no_config_required
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
#
|
57
|
+
# Specify name of configuration values used in plugin implementation.
|
58
|
+
#
|
59
|
+
#
|
60
|
+
def agent_config_options *args
|
61
|
+
@config_options_list||=[]
|
62
|
+
args.each do |config|
|
63
|
+
attr_accessor config
|
64
|
+
@config_options_list << config
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
#
|
70
|
+
# Instance Info
|
71
|
+
#
|
72
|
+
attr_reader :name,:agent_info
|
73
|
+
def guid
|
74
|
+
return @guid if @guid
|
75
|
+
@guid=self.class.guid
|
76
|
+
#
|
77
|
+
# Verify GUID is set correctly...
|
78
|
+
#
|
79
|
+
if @guid=="guid" or @guid=="_TYPE_YOUR_GUID_HERE_"
|
80
|
+
#this feels tightly coupled to the config class, testing this is difficult, thinking about refactoring (NH)
|
81
|
+
@guid = NewRelic::Plugin::Config.config.newrelic['guids'][agent_info[:ident].to_s] if NewRelic::Plugin::Config.config.newrelic['guids']
|
82
|
+
puts "NOTE: GUID updated for #{instance_label} at run-time to '#{@guid}'"
|
83
|
+
end
|
84
|
+
raise "Did not set GUID" if @guid.nil? or @guid=="" or @guid=="guid" or @guid=="_TYPE_YOUR_GUID_HERE_"
|
85
|
+
@guid
|
86
|
+
end
|
87
|
+
def version
|
88
|
+
self.class.version
|
89
|
+
end
|
90
|
+
def label
|
91
|
+
self.class.label
|
92
|
+
end
|
93
|
+
#
|
94
|
+
# Instantiate a newrelic_plugin instance
|
95
|
+
def initialize name,agent_info,options={}
|
96
|
+
@name=name
|
97
|
+
@agent_info=agent_info
|
98
|
+
@ident=agent_info[:ident]
|
99
|
+
@options=options
|
100
|
+
if self.class.config_options_list
|
101
|
+
self.class.config_options_list.each do |config|
|
102
|
+
self.send("#{config}=",options[config])
|
103
|
+
end
|
104
|
+
end
|
105
|
+
@last_time=nil
|
106
|
+
|
107
|
+
#
|
108
|
+
# Run agent-specific metric setup, if necessary
|
109
|
+
setup_metrics if respond_to? :setup_metrics
|
110
|
+
end
|
111
|
+
def instance_label
|
112
|
+
if !respond_to? :instance_label_proc_method
|
113
|
+
mod=Module.new
|
114
|
+
mod.send :define_method,:instance_label_proc_method,self.class.instance_label_proc
|
115
|
+
self.extend mod
|
116
|
+
end
|
117
|
+
self.instance_label_proc_method
|
118
|
+
end
|
119
|
+
|
120
|
+
#
|
121
|
+
#
|
122
|
+
# Setup & Report Metrics
|
123
|
+
#
|
124
|
+
#
|
125
|
+
def report_metric metric_name,units,value,opts={}
|
126
|
+
return if value.nil?
|
127
|
+
@data_collector.add_data metric_name,units,value.to_f,opts
|
128
|
+
end
|
129
|
+
|
130
|
+
#
|
131
|
+
#
|
132
|
+
# Execute a poll cycle
|
133
|
+
#
|
134
|
+
#
|
135
|
+
def run poll_interval
|
136
|
+
#
|
137
|
+
# Start of cycle work, if any
|
138
|
+
cycle_start if respond_to? :cycle_start
|
139
|
+
|
140
|
+
#
|
141
|
+
# Collect Data
|
142
|
+
@data_collector=DataCollector.new self,poll_interval
|
143
|
+
poll_cycle
|
144
|
+
cnt=@data_collector.process
|
145
|
+
@data_collector=nil
|
146
|
+
|
147
|
+
#
|
148
|
+
# End of cycle work, if any
|
149
|
+
cycle_end if respond_to? :cycle_end
|
150
|
+
cnt
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
module NewRelic
|
3
|
+
module Plugin
|
4
|
+
#
|
5
|
+
#
|
6
|
+
# Process global config file for NewRelic. This is defined per execution environment.
|
7
|
+
#
|
8
|
+
# Author:: Lee Atchison <lee@newrelic.com>
|
9
|
+
# Copyright:: Copyright (c) 2012 New Relic, Inc.
|
10
|
+
#
|
11
|
+
# The system configuration can be accessed as a hash using this method. For example:
|
12
|
+
#
|
13
|
+
# NewRelic::Plugin::Config.config.options['xxx']
|
14
|
+
# or:
|
15
|
+
# NewRelic::Plugin::Config.config.newrelic['...']
|
16
|
+
# or:
|
17
|
+
# NewRelic::Plugin::Config.config.agents['...']
|
18
|
+
#
|
19
|
+
#
|
20
|
+
class Config
|
21
|
+
attr_reader :options
|
22
|
+
# Creates an instance of a configuration object, loading the configuration
|
23
|
+
# from the YAML configuration file. Note: this method should not be used
|
24
|
+
# directly, rather the global method +config+ should be referenced instead.
|
25
|
+
def initialize
|
26
|
+
@options = YAML::load(Config.config_yaml) if Config.config_yaml
|
27
|
+
@options = YAML.load_file(Config.config_file) unless @options
|
28
|
+
end
|
29
|
+
|
30
|
+
#
|
31
|
+
# Return a hash of NewRelic configuration information.
|
32
|
+
#
|
33
|
+
# Usage: NewRelic::Plugin::Config.config.newrelic[...]
|
34
|
+
#
|
35
|
+
def newrelic
|
36
|
+
@options["newrelic"]
|
37
|
+
end
|
38
|
+
#
|
39
|
+
# Return a hash of agent configuration information.
|
40
|
+
#
|
41
|
+
# Usage: NewRelic::Plugin::Config.config.agents[...]
|
42
|
+
#
|
43
|
+
def agents
|
44
|
+
@options["agents"]
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
# Set the name of the configuration file:
|
49
|
+
#
|
50
|
+
# NewRelic::Plugin::Config.config_file="lib/config.yml"
|
51
|
+
#
|
52
|
+
def self.config_file= config_file
|
53
|
+
@config_file=config_file
|
54
|
+
@config_yaml=nil
|
55
|
+
@instance=nil
|
56
|
+
end
|
57
|
+
def self.config_file
|
58
|
+
@config_file||="config/newrelic_plugin.yml"
|
59
|
+
end
|
60
|
+
#
|
61
|
+
# Set the content of the configuration as a YAML string
|
62
|
+
#
|
63
|
+
# NewRelic::Plugin::Config.config_yaml="newrelic:\n test: 2\n test2: four\nagents:\n test: 4\n test2: six\n"
|
64
|
+
#
|
65
|
+
def self.config_yaml= config_yaml
|
66
|
+
@config_yaml=config_yaml
|
67
|
+
@config_file=nil
|
68
|
+
@instance=nil
|
69
|
+
end
|
70
|
+
def self.config_yaml
|
71
|
+
@config_yaml
|
72
|
+
end
|
73
|
+
#
|
74
|
+
# Returns a memoized version of the configuration object.
|
75
|
+
#
|
76
|
+
def self.config
|
77
|
+
@instance||=NewRelic::Plugin::Config.new
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
def plugin_config
|
83
|
+
NewRelic::Plugin::Config.config
|
84
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module NewRelic
|
2
|
+
module Plugin
|
3
|
+
#
|
4
|
+
# Gather's data from agent and presents it to nr_connect for processing.
|
5
|
+
#
|
6
|
+
# This is a wrapper for nr_connect used in the context of an agent.
|
7
|
+
#
|
8
|
+
class DataCollector
|
9
|
+
#
|
10
|
+
#
|
11
|
+
# Methods used within an agent implementation:
|
12
|
+
#
|
13
|
+
#
|
14
|
+
def add_data metric_name,units,data,opts={}
|
15
|
+
nrmsg.add_stat_fullname(
|
16
|
+
"Component/#{metric_name}[#{units}]",
|
17
|
+
1,
|
18
|
+
data,
|
19
|
+
:min => data,
|
20
|
+
:max => data,
|
21
|
+
:sum_of_squares => (data*data)
|
22
|
+
)
|
23
|
+
#puts "Metric '#{metric_name}' (#{units}): raw: #{value}, processed: #{data} (#{processor_type})" if verbose?
|
24
|
+
@cnt+=1
|
25
|
+
data
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
#
|
30
|
+
# Internal methods used within Agent class
|
31
|
+
#
|
32
|
+
def initialize agent,poll_interval
|
33
|
+
@agent=agent
|
34
|
+
@poll_interval=poll_interval
|
35
|
+
@cnt=0
|
36
|
+
end
|
37
|
+
def process
|
38
|
+
nrmsg_send
|
39
|
+
@cnt
|
40
|
+
end
|
41
|
+
|
42
|
+
################################################################################
|
43
|
+
private
|
44
|
+
|
45
|
+
def verbose?
|
46
|
+
return @verbose unless @verbose.nil?
|
47
|
+
@verbose=NewRelic::Plugin::Config.config.newrelic["verbose"].to_i>0
|
48
|
+
end
|
49
|
+
|
50
|
+
#
|
51
|
+
# NewRelicConnection Handling
|
52
|
+
#
|
53
|
+
def nrmsg
|
54
|
+
@nrmsg||=nrobj.start_message @agent.instance_label,@agent.guid,@agent.version,@poll_interval
|
55
|
+
end
|
56
|
+
def nrmsg_send
|
57
|
+
return unless @nrmsg
|
58
|
+
@nrmsg.send_metrics
|
59
|
+
@nrmsg=nil
|
60
|
+
end
|
61
|
+
def nrobj
|
62
|
+
@nrobj||=NewRelic::Plugin::NewRelicConnection.new NewRelic::Plugin::Config.config.newrelic
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module NewRelic
|
2
|
+
|
3
|
+
# Standard Errors
|
4
|
+
#
|
5
|
+
# Author:: Lee Atchison <lee@newrelic.com>
|
6
|
+
# Copyright:: Copyright (c) 2012 New Relic, Inc.
|
7
|
+
#
|
8
|
+
module Plugin # :nodoc:
|
9
|
+
class AgentError<Exception;end
|
10
|
+
class NoAgents<Exception;end
|
11
|
+
class NoMetrics<Exception;end
|
12
|
+
class NewRelicCommError<Exception;end
|
13
|
+
class UnknownInstalledAgent<AgentError;end
|
14
|
+
class UnknownInstalledProcessor<AgentError;end
|
15
|
+
class CouldNotInitializeAgent<AgentError;end
|
16
|
+
class CouldNotInitializeProcessor<AgentError;end
|
17
|
+
class BadConfig<AgentError;end
|
18
|
+
class NoResultReturned<AgentError;end
|
19
|
+
class NoDataReturned<AgentError;end
|
20
|
+
class InvalidMetricValueType<AgentError;end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
|
3
|
+
module NewRelic
|
4
|
+
module Plugin
|
5
|
+
class NewRelicConnection
|
6
|
+
#
|
7
|
+
# Allowed configuration Values:
|
8
|
+
# license_key [required] Specifies RPM account to store metrics in
|
9
|
+
# host [optional] override default production collector
|
10
|
+
# port [optional] override default production collector
|
11
|
+
# verbose [optional] log HTTP transactions to stdout
|
12
|
+
#
|
13
|
+
def initialize config = {}
|
14
|
+
@config = config
|
15
|
+
end
|
16
|
+
#
|
17
|
+
# Start creating a message containing a set of metrics to New Relic.
|
18
|
+
#
|
19
|
+
def start_message component_name,component_guid,component_version,duration_in_seconds
|
20
|
+
NewRelic::Plugin::NewRelicMessage.new self,component_name,component_guid,component_version,duration_in_seconds
|
21
|
+
end
|
22
|
+
|
23
|
+
#################### Internal Only
|
24
|
+
#
|
25
|
+
# Create HTTP tunnel to New Relic
|
26
|
+
#
|
27
|
+
def connect
|
28
|
+
@connect ||= Faraday.new(:url => url, :ssl => { :verify => ssl_host_verification } ) do |builder|
|
29
|
+
builder.request :url_encoded
|
30
|
+
builder.response :logger if @config["log_http"].to_i>0
|
31
|
+
builder.use AuthenticationMiddleware,license_key
|
32
|
+
builder.adapter :net_http
|
33
|
+
end
|
34
|
+
end
|
35
|
+
def url
|
36
|
+
@config["endpoint"] || "https://platform-api.newrelic.com"
|
37
|
+
end
|
38
|
+
def uri
|
39
|
+
@config["uri"] || "/platform/v1/metrics"
|
40
|
+
end
|
41
|
+
def ssl_host_verification
|
42
|
+
ssl_host_verification = @config["ssl_host_verification"]
|
43
|
+
return true if ssl_host_verification.nil?
|
44
|
+
!!ssl_host_verification
|
45
|
+
end
|
46
|
+
class AuthenticationMiddleware < Faraday::Middleware
|
47
|
+
def initialize(app,apikey)
|
48
|
+
super(app)
|
49
|
+
@apikey = apikey
|
50
|
+
end
|
51
|
+
def call(env)
|
52
|
+
unless env[:request_headers]['X-License-Key']
|
53
|
+
env[:request_headers]['X-License-Key'] = @apikey
|
54
|
+
end
|
55
|
+
@app.call(env)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
def log_metrics?
|
59
|
+
@config["verbose"] || @config["log_metrics"]
|
60
|
+
end
|
61
|
+
def license_key
|
62
|
+
@config["license_key"]
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|