newrelic-elasticsearch 0.1.0

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