newrelic-elasticsearch 0.1.0

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4ed7177296564fd7bae9ce3b4bc19d4b3d19f12d
4
+ data.tar.gz: 220ff74651363c4d8188b5928c2fa9f83c01503d
5
+ SHA512:
6
+ metadata.gz: 064c15be05f084f92ce1cd6a9456deb8e0d8e80d831aabc1e261ab86cee55ce1ef5d3c57dfdf25dc4880baee5d33fff9775e22c3c4fff1067364d04756020027
7
+ data.tar.gz: bae067d77f372639775c6048b313ecfd8b96340eb6e9cd5d372ebc1cd6b10b78fcf04462d0c53955825dd89a26e9440fe8dd8b97537e5b788874cb84de950269
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /log/
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in newrelic-elasticsearch.gemspec
4
+ gemspec
5
+
6
+ gem 'pry-byebug'
data/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # Newrelic::Elasticsearch
2
+
3
+ Provides NewRelic instrumentation for Elasticsearch for Application Level concerns. This is not
4
+ an Elasticsearch monitoring tool, but provides metrics for application performance related
5
+ to Elasticsearch queries.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'new_relic-elasticsearch'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install new_relic-elasticsearch
22
+
23
+ ## Usage
24
+
25
+ Add it to your Gemfile, take it away.
26
+
27
+ ## Contributing
28
+
29
+ Bug reports and pull requests are welcome on GitHub at https://github.com/goldstar/newrelic-elasticsearch.
30
+
data/Rakefile ADDED
@@ -0,0 +1,38 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ task :default => :test
5
+
6
+ task :test do
7
+ Rake::TestTask.new do |t|
8
+ t.libs << 'test'
9
+ t.pattern = "test/**/*_test.rb"
10
+ end
11
+ end
12
+
13
+ task :generate_resolver do
14
+ list = File.read(File.expand_path('test/endpoint_list.txt', __dir__))
15
+
16
+ pp list.lines.each.with_object({}) { |l, memo|
17
+ scanner = StringScanner.new(l)
18
+ scanner.scan_until(/(POST|GET|PUT|DELETE|HEAD)/)
19
+ next if scanner.pre_match.nil?
20
+
21
+ operation_name = scanner.pre_match.rstrip
22
+ method = scanner.matched
23
+ url_pattern = scanner.rest
24
+ api_name = if matches = /\/(_.*?)(\/|\b)/.match(url_pattern)
25
+ matches[1]
26
+ end
27
+
28
+
29
+ if memo.has_key?([method, api_name])
30
+ next if memo[[method, api_name]] == operation_name
31
+ memo[[method, api_name]] = :"ambiguous#{api_name || "_" + method.downcase}_resolver"
32
+ else
33
+ memo[[method, api_name]] = operation_name
34
+ end
35
+
36
+ }.sort_by { |k,v| k[1] || '_' }.to_h
37
+ end
38
+
@@ -0,0 +1,46 @@
1
+ require 'newrelic/elasticsearch/version'
2
+ require 'new_relic/agent/method_tracer'
3
+ require 'elasticsearch'
4
+
5
+ DependencyDetection.defer do
6
+ named :elasticsearch
7
+
8
+ depends_on do
9
+ defined?(::Elasticsearch::Transport::Client)
10
+ end
11
+
12
+ executes do
13
+ ::NewRelic::Agent.logger.info 'Installing Elasticsearch instrumentation'
14
+ require 'newrelic/elasticsearch/operation_resolver'
15
+ end
16
+
17
+ executes do
18
+ NewRelic::Agent::MethodTracer.extend(NewRelic::Agent::MethodTracer)
19
+
20
+ ::Elasticsearch::Transport::Client.class_eval do
21
+ def perform_request_with_new_relic(method, path, params={}, body=nil)
22
+ resolver = NewRelic::ElasticsearchOperationResolver.new(method, path)
23
+
24
+ callback = proc do |result, metric, elapsed|
25
+ # conditionally require body with notice_statement
26
+ if body && params
27
+ statement = body.merge(params)
28
+ else
29
+ statement = body || params
30
+ end
31
+ statement[:scope] = resolver.scope
32
+ statement[:additional_parameters] = resolver.operands
33
+
34
+ NewRelic::Agent::Datastores.notice_statement(statement.inspect, elapsed) if statement
35
+ end
36
+
37
+ NewRelic::Agent::Datastores.wrap('Elasticsearch', resolver.operation_name, resolver.index, callback) do
38
+ perform_request_without_new_relic(method, path, params, body)
39
+ end
40
+ end
41
+
42
+ alias_method :perform_request_without_new_relic, :perform_request
43
+ alias_method :perform_request, :perform_request_with_new_relic
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,216 @@
1
+ class NewRelic::ElasticsearchOperationResolver
2
+ ELASTICSEARCH_OPERATION_NAMES =
3
+ {
4
+ [ "PUT", nil ] => :ambiguous_put_resolver,
5
+ [ "POST", nil ] => :ambiguous_post_resolver,
6
+ [ "HEAD", nil ] => :ambiguous_head_resolver,
7
+ [ "GET", nil ] => :ambiguous_get_resolver,
8
+ [ "DELETE", nil ] => :ambiguous_delete_resolver,
9
+ [ "HEAD", "_alias" ] => "AliasesExist",
10
+ [ "PUT", "_alias" ] => "IndexPutAlias",
11
+ [ "DELETE", "_alias" ] => "IndexDeleteAliases",
12
+ [ "GET", "_alias" ] => "GetAliases",
13
+ [ "GET", "_aliases" ] => "GetIndicesAliases",
14
+ [ "POST", "_aliases" ] => "IndicesAliases",
15
+ [ "GET", "_analyze" ] => "Analyze",
16
+ [ "POST", "_analyze" ] => "Analyze",
17
+ [ "POST", "_bulk" ] => "Bulk",
18
+ [ "PUT", "_bulk" ] => "Bulk",
19
+ [ "GET", "_cache" ] => "ClearIndicesCache",
20
+ [ "POST", "_cache" ] => "ClearIndicesCache",
21
+ [ "GET", "_cat" ] => :ambiguous_cat_resolver,
22
+ [ "POST", "_close" ] => "CloseIndex",
23
+ [ "POST", "_cluster" ] => "ClusterReroute",
24
+ [ "GET", "_cluster" ] => :ambigous_cluster_resolver,
25
+ [ "PUT", "_cluster" ] => "ClusterUpdateSettings",
26
+ [ "GET", "_count" ] => "Count",
27
+ [ "POST", "_count" ] => "Count",
28
+ [ "POST", "_create" ] => "CreateDocument",
29
+ [ "PUT", "_create" ] => "CreateDocument",
30
+ [ "GET", "_explain" ] => "Explain",
31
+ [ "POST", "_explain" ] => "Explain",
32
+ [ "GET", "_flush" ] => "Flush",
33
+ [ "POST", "_flush" ] => "Flush",
34
+ [ "POST", "_gateway" ] => "GatewaySnapshot",
35
+ [ "GET", "_id" ] => "SearchScroll",
36
+ [ "DELETE", "_id" ] => "ClearScroll",
37
+ [ "POST", "_id" ] => "SearchScroll",
38
+ [ "POST", "_mapping" ] => "PutMapping",
39
+ [ "PUT", "_mapping" ] => "PutMapping",
40
+ [ "GET", "_mapping" ] => "GetMapping",
41
+ [ "DELETE", "_mapping" ] => "DeleteMapping",
42
+ [ "POST", "_mget" ] => "MultiGet",
43
+ [ "GET", "_mget" ] => "MultiGet",
44
+ [ "GET", "_mlt" ] => "MoreLikeThis",
45
+ [ "POST", "_mlt" ] => "MoreLikeThis",
46
+ [ "POST", "_mpercolate" ] => "MultiPercolate",
47
+ [ "POST", "_msearch" ] => "MultiSearch",
48
+ [ "GET", "_msearch" ] => "MultiSearch",
49
+ [ "POST", "_mtermvectors" ] => "MultiTermVectors",
50
+ [ "GET", "_mtermvectors" ] => "MultiTermVectors",
51
+ [ "GET", "_nodes" ] => :ambiguous_nodes_resolver,
52
+ [ "POST", "_open" ] => "OpenIndex",
53
+ [ "POST", "_optimize" ] => "Optimize",
54
+ [ "GET", "_optimize" ] => "Optimize",
55
+ [ "GET", "_percolate" ] => :ambiguous_percolate_resolver,
56
+ [ "POST", "_percolate" ] => :ambiguous_percolate_resolver,
57
+ [ "DELETE", "_query" ] => "DeleteByQuery",
58
+ [ "GET", "_refresh" ] => "Refresh",
59
+ [ "POST", "_refresh" ] => "Refresh",
60
+ [ "POST", "_reindex" ] => "Reindex",
61
+ [ "POST", "_restart" ] => "NodesRestart",
62
+ [ "GET", "_search" ] => :ambiguous_search_resolver,
63
+ [ "POST", "_search" ] => :ambiguous_search_resolver,
64
+ [ "DELETE", "_search" ] => "ClearScroll",
65
+ [ "GET", "_segments" ] => "IndicesSegments",
66
+ [ "PUT", "_settings" ] => "UpdateSettings",
67
+ [ "GET", "_settings" ] => "GetSettings",
68
+ [ "GET", "_search_shards" ] => "ClusterSearchShards",
69
+ [ "POST", "_search__shards" ] => "ClusterSearchShards",
70
+ [ "POST", "_shutdown" ] => "NodesShutdown",
71
+ [ "HEAD", "_source" ] => "HeadSource",
72
+ [ "GET", "_source" ] => "GetSource",
73
+ [ "GET", "_stats" ] => :ambiguous_stats_resolver,
74
+ [ "GET", "_status" ] => "IndicesStatus",
75
+ [ "GET", "_suggest" ] => "Suggest",
76
+ [ "POST", "_suggest" ] => "Suggest",
77
+ [ "POST", "_template" ] => "PutIndexTemplate",
78
+ [ "PUT", "_template" ] => "PutIndexTemplate",
79
+ [ "DELETE", "_template" ] => "DeleteIndexTemplate",
80
+ [ "GET", "_template" ] => "GetIndexTemplate",
81
+ [ "HEAD", "_template" ] => "HeadIndexTemplate",
82
+ [ "GET", "_termvector" ] => "TermVector",
83
+ [ "POST", "_termvector" ] => "TermVector",
84
+ [ "GET", "_threads" ] => "NodesHotThreads",
85
+ [ "POST", "_update" ] => "Update",
86
+ [ "GET", "_validate" ] => "ValidateQuery",
87
+ [ "POST", "_validate" ] => "ValidateQuery",
88
+ [ "GET", "_warmer" ] => "GetWarmer",
89
+ [ "PUT", "_warmer" ] => "PutWarmer",
90
+ [ "DELETE", "_warmer" ] => "DeleteWarmer"
91
+ }
92
+
93
+ AMBIGUOUS_API_OPS = {
94
+ 0 => 'Server',
95
+ 1 => 'Index',
96
+ 2 => 'Type',
97
+ 3 => 'Document'
98
+ }
99
+
100
+ AMBIGOUS_CAT_OPS = {
101
+ "indices" => "Indices",
102
+ "master" => "Cluster",
103
+ "nodes" => "Nodes",
104
+ "shards" => "Shards",
105
+ "aliases" => "Aliases"
106
+ }
107
+
108
+ attr_accessor :http_method, :path
109
+
110
+ def initialize(http_method, path)
111
+ @http_method = http_method
112
+ @path = path
113
+ end
114
+
115
+ def operation_name
116
+ resolved = ELASTICSEARCH_OPERATION_NAMES[[http_method, api_name]]
117
+ case resolved
118
+ when Symbol
119
+ send(resolved)
120
+ else
121
+ resolved
122
+ end
123
+ end
124
+
125
+ def path_components
126
+ @path_components ||= path.split('/').reject { |s| s.empty? }
127
+ end
128
+
129
+ def operands
130
+ if api_name.nil?
131
+ []
132
+ else
133
+ path_components[op_index + 1 .. -1]
134
+ end
135
+ end
136
+
137
+ def api_name
138
+ return nil if op_index.nil?
139
+ path_components[op_index]
140
+ end
141
+
142
+ def scope
143
+ if api_name.nil?
144
+ path_components
145
+ else
146
+ path_components[0 .. (op_index - 1)]
147
+ end
148
+ end
149
+
150
+ def scope_path
151
+ scope.join("_")
152
+ end
153
+
154
+ def index
155
+ scope[0]
156
+ end
157
+
158
+ def type
159
+ scope[1]
160
+ end
161
+
162
+ def id
163
+ scope[2]
164
+ end
165
+
166
+ def op_index
167
+ @op_index ||= path_components.index { |c| c.start_with?('_') }
168
+ end
169
+
170
+ def ambiguous_put_resolver
171
+ AMBIGUOUS_API_OPS[scope.count] + "Create"
172
+ end
173
+
174
+ alias_method :ambiguous_post_resolver, :ambiguous_put_resolver
175
+
176
+ def ambiguous_head_resolver
177
+ AMBIGUOUS_API_OPS[scope.count] + "Exists"
178
+ end
179
+
180
+ def ambiguous_get_resolver
181
+ AMBIGUOUS_API_OPS[scope.count] + "Get"
182
+ end
183
+
184
+ def ambiguous_delete_resolver
185
+ AMBIGUOUS_API_OPS[scope.count] + "Delete"
186
+ end
187
+
188
+ def ambiguous_cat_resolver
189
+ "Cat" + AMBIGOUS_CAT_OPS[operands.first]
190
+ end
191
+
192
+ def ambiguous_nodes_resolver
193
+ "Node" + operands[1..-1].map(&:capitalize).join
194
+ end
195
+
196
+ def ambiguous_percolate_resolver
197
+ "Percolate" + operands.first.to_s
198
+ end
199
+
200
+ def ambiguous_search_resolver
201
+ "Search" + operands.first.to_s
202
+ end
203
+
204
+ def ambiguous_stats_resolver
205
+ "Indicies" + operands.map(&:capitalize).join
206
+ end
207
+
208
+ def ambiguous_cluster_resolver
209
+ case operands.join('/')
210
+ when /pending_tasks/ then "ClusterPendingTasks"
211
+ when /nodes\/_restart/ then "NodesRestart"
212
+ when /nodes\/(.*?)\/_restart/ then "NodesRestart"
213
+ when /nodes\/(>*?)\/_shutdown/ then "NodesShutdown"
214
+ end
215
+ end
216
+ end
@@ -0,0 +1,5 @@
1
+ module NewRelic
2
+ module Elasticsearch
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'newrelic/elasticsearch/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "newrelic-elasticsearch"
8
+ spec.version = NewRelic::Elasticsearch::VERSION
9
+ spec.authors = ["Stephen Prater"]
10
+ spec.email = ["me@stephenprater.com"]
11
+
12
+ spec.summary = %q{Provides NewRelic datastore instrumentation for Elasticsearch}
13
+ spec.description = %q{Not for monitoring Elasticsearch, but for instrumenting it as
14
+ as a datatore in the databases tab in NewRelic}
15
+ spec.homepage = "http://github.com/goldstar/newrelic-elasticsearch"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.12"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency "minitest", "~> 4.7.5"
25
+ spec.add_development_dependency "webmock"
26
+ spec.add_dependency "newrelic_rpm"
27
+ spec.add_dependency "elasticsearch"
28
+ end
metadata ADDED
@@ -0,0 +1,138 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: newrelic-elasticsearch
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Stephen Prater
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-07-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.12'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.12'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 4.7.5
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 4.7.5
55
+ - !ruby/object:Gem::Dependency
56
+ name: webmock
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: newrelic_rpm
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: elasticsearch
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: |-
98
+ Not for monitoring Elasticsearch, but for instrumenting it as
99
+ as a datatore in the databases tab in NewRelic
100
+ email:
101
+ - me@stephenprater.com
102
+ executables: []
103
+ extensions: []
104
+ extra_rdoc_files: []
105
+ files:
106
+ - ".gitignore"
107
+ - Gemfile
108
+ - README.md
109
+ - Rakefile
110
+ - lib/newrelic/elasticsearch.rb
111
+ - lib/newrelic/elasticsearch/operation_resolver.rb
112
+ - lib/newrelic/elasticsearch/version.rb
113
+ - newrelic-elasticsearch.gemspec
114
+ homepage: http://github.com/goldstar/newrelic-elasticsearch
115
+ licenses: []
116
+ metadata: {}
117
+ post_install_message:
118
+ rdoc_options: []
119
+ require_paths:
120
+ - lib
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ requirements: []
132
+ rubyforge_project:
133
+ rubygems_version: 2.5.1
134
+ signing_key:
135
+ specification_version: 4
136
+ summary: Provides NewRelic datastore instrumentation for Elasticsearch
137
+ test_files: []
138
+ has_rdoc: