elastic-apm 3.7.0 → 3.11.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 +4 -4
- data/.ci/Jenkinsfile +139 -96
- data/.ci/packer_cache.sh +12 -10
- data/.rspec +0 -1
- data/CHANGELOG.asciidoc +80 -0
- data/Gemfile +4 -3
- data/bin/run-tests +4 -1
- data/docker-compose.yml +2 -0
- data/docs/configuration.asciidoc +38 -0
- data/docs/debugging.asciidoc +14 -0
- data/docs/supported-technologies.asciidoc +2 -1
- data/lib/elastic_apm/config.rb +36 -13
- data/lib/elastic_apm/config/options.rb +2 -1
- data/lib/elastic_apm/config/round_float.rb +31 -0
- data/lib/elastic_apm/config/wildcard_pattern_list.rb +13 -1
- data/lib/elastic_apm/context_builder.rb +1 -1
- data/lib/elastic_apm/grpc.rb +2 -2
- data/lib/elastic_apm/instrumenter.rb +10 -3
- data/lib/elastic_apm/metadata.rb +3 -1
- data/lib/elastic_apm/metadata/cloud_info.rb +128 -0
- data/lib/elastic_apm/metadata/service_info.rb +5 -2
- data/lib/elastic_apm/metadata/system_info.rb +5 -3
- data/lib/elastic_apm/metadata/system_info/container_info.rb +28 -4
- data/lib/elastic_apm/middleware.rb +8 -2
- data/lib/elastic_apm/opentracing.rb +47 -23
- data/lib/elastic_apm/span.rb +7 -3
- data/lib/elastic_apm/spies.rb +16 -14
- data/lib/elastic_apm/spies/delayed_job.rb +4 -2
- data/lib/elastic_apm/spies/dynamo_db.rb +58 -0
- data/lib/elastic_apm/spies/elasticsearch.rb +26 -2
- data/lib/elastic_apm/spies/mongo.rb +1 -1
- data/lib/elastic_apm/spies/net_http.rb +6 -2
- data/lib/elastic_apm/spies/sequel.rb +1 -1
- data/lib/elastic_apm/trace_context.rb +1 -1
- data/lib/elastic_apm/trace_context/traceparent.rb +2 -4
- data/lib/elastic_apm/trace_context/tracestate.rb +112 -9
- data/lib/elastic_apm/transaction.rb +26 -5
- data/lib/elastic_apm/transport/connection.rb +1 -0
- data/lib/elastic_apm/transport/filters/hash_sanitizer.rb +70 -0
- data/lib/elastic_apm/transport/filters/secrets_filter.rb +14 -56
- data/lib/elastic_apm/transport/serializers.rb +8 -6
- data/lib/elastic_apm/transport/serializers/metadata_serializer.rb +56 -23
- data/lib/elastic_apm/transport/serializers/span_serializer.rb +2 -1
- data/lib/elastic_apm/transport/serializers/transaction_serializer.rb +1 -0
- data/lib/elastic_apm/transport/user_agent.rb +3 -3
- data/lib/elastic_apm/transport/worker.rb +5 -0
- data/lib/elastic_apm/util.rb +2 -0
- data/lib/elastic_apm/util/precision_validator.rb +46 -0
- data/lib/elastic_apm/version.rb +1 -1
- metadata +12 -8
- data/.ci/downstreamTests.groovy +0 -192
data/bin/run-tests
CHANGED
@@ -8,7 +8,10 @@ runRspec(){
|
|
8
8
|
if [ -n "${case}" ]; then
|
9
9
|
bn="$(basename ${case} _spec.rb)/"
|
10
10
|
fi
|
11
|
-
bundle exec rspec
|
11
|
+
bundle exec rspec \
|
12
|
+
-f progress \
|
13
|
+
-r yarjuf -f JUnit -o spec/junit-reports/${bn}ruby-agent-junit.xml \
|
14
|
+
${case}
|
12
15
|
}
|
13
16
|
specific_spec=$1
|
14
17
|
|
data/docker-compose.yml
CHANGED
data/docs/configuration.asciidoc
CHANGED
@@ -287,6 +287,16 @@ If your service handles data like this, we advise to only enable this feature wi
|
|
287
287
|
|
288
288
|
Whether or not to attach the request headers to transactions and errors.
|
289
289
|
|
290
|
+
[float]
|
291
|
+
[[config-capture-elasticsearch-queries]]
|
292
|
+
==== `capture_elasticsearch_queries`
|
293
|
+
|============
|
294
|
+
| Environment | `Config` key | Default
|
295
|
+
| `ELASTIC_APM_CAPTURE_ELASTICSEARCH_QUERIES` | `capture_elasticsearch_queries` | `false`
|
296
|
+
|============
|
297
|
+
|
298
|
+
Whether or not to capture the body from requests in Elasticsearch.
|
299
|
+
|
290
300
|
[float]
|
291
301
|
[[config-capture-env]]
|
292
302
|
==== `capture_env`
|
@@ -707,6 +717,23 @@ NOTE: The service name must conform to this regular expression: `^[a-zA-Z0-9 _-]
|
|
707
717
|
In layman's terms: Your service name must only contain characters from the ASCII
|
708
718
|
alphabet, numbers, dashes, underscores and spaces.
|
709
719
|
|
720
|
+
[float]
|
721
|
+
[[config-service-node-name]]
|
722
|
+
==== `service_node_name`
|
723
|
+
|
724
|
+
[options="header"]
|
725
|
+
|============
|
726
|
+
| Environment | `Config` key | Default | Example
|
727
|
+
| `ELASTIC_APM_SERVICE_NODE_NAME` | `service_node_name` | `nil` | `"my-app-1"`
|
728
|
+
|============
|
729
|
+
|
730
|
+
The name of the given service node. This is optional, and if omitted, the APM
|
731
|
+
Server will fall back on `system.container.id` if available, and finally
|
732
|
+
`host.name` if necessary.
|
733
|
+
|
734
|
+
This option allows you to set the node name manually to ensure uniqueness and
|
735
|
+
meaningfulness.
|
736
|
+
|
710
737
|
[float]
|
711
738
|
[[config-service-version]]
|
712
739
|
==== `service_version`
|
@@ -826,6 +853,7 @@ To reduce overhead and storage requirements, you can set the sample rate to a va
|
|
826
853
|
between `0.0` and `1.0`.
|
827
854
|
We still record overall time and the result for unsampled transactions, but no
|
828
855
|
context information, tags, or spans.
|
856
|
+
Note that the sample rate will be rounded to 4 digits of precision.
|
829
857
|
|
830
858
|
[float]
|
831
859
|
[[config-use-experimental-sql-parser]]
|
@@ -890,3 +918,13 @@ The unit is provided as suffix directly after the number, without any separation
|
|
890
918
|
* `gb` (gigabytes)
|
891
919
|
|
892
920
|
NOTE: we use the power-of-two sizing convention, e.g. `1 kilobyte == 1024 bytes`
|
921
|
+
|
922
|
+
[float]
|
923
|
+
[[special-configuration]]
|
924
|
+
=== Special configuration
|
925
|
+
|
926
|
+
Elastic APM patches `Kernel#require` to auto-detect and instrument supported third party libraries. It does so with the utmost care but in rare cases it can conflict with some libraries.
|
927
|
+
|
928
|
+
To get around this patch, set the environment variable `ELASTIC_APM_SKIP_REQUIRE_PATCH` to `"1"`.
|
929
|
+
|
930
|
+
If you choose to do so, the agent might need some additional tweaking to make sure the third party libraries are picked up and instrumented. Make sure you require the agent _after_ you require your other dependencies.
|
data/docs/debugging.asciidoc
CHANGED
@@ -28,3 +28,17 @@ Things to consider:
|
|
28
28
|
- Experiencing high load? The agent can spawn multiple instances of its Workers that pick off the queue by changing the option `pool_size` (default is `1`).
|
29
29
|
- If you have high load you may also consider setting `transaction_sample_rate` to something smaller than `1.0`. This determines whether to include _spans_ for every _transaction_. If you have enough traffic, skipping some (probably) identical spans won't have a noticeable effect on your data.
|
30
30
|
|
31
|
+
[float]
|
32
|
+
[[disable-agent]]
|
33
|
+
=== Disable the Agent
|
34
|
+
|
35
|
+
In the unlikely event the agent causes disruptions to a production application,
|
36
|
+
you can disable the agent while you troubleshoot.
|
37
|
+
|
38
|
+
If you have access to <<dynamic-configuration,dynamic configuration>>,
|
39
|
+
you can disable the recording of events by setting <<config-recording,`recording`>> to `false`.
|
40
|
+
When changed at runtime from a supported source, there's no need to restart your application.
|
41
|
+
|
42
|
+
If that doesn't work, or you don't have access to dynamic configuration, you can disable the agent by setting
|
43
|
+
<<config-enabled,`enabled`>> to `false`.
|
44
|
+
You'll need to restart your application for the changes to apply.
|
@@ -60,6 +60,7 @@ See <<getting-started-grape>>.
|
|
60
60
|
We automatically instrument database actions using:
|
61
61
|
|
62
62
|
- ActiveRecord (v4.2+)
|
63
|
+
- DynamoDB (v1.0+)
|
63
64
|
- Elasticsearch (v0.9+)
|
64
65
|
- Mongo (v2.1+)
|
65
66
|
- Redis (v3.1+)
|
@@ -151,4 +152,4 @@ To instrument a server, add the `ElasticAPM::GRPC::ServerInterceptor`.
|
|
151
152
|
[source,ruby]
|
152
153
|
----
|
153
154
|
GRPC::RpcServer.new(interceptors: [ElasticAPM::GRPC::ServerInterceptor.new])
|
154
|
-
----
|
155
|
+
----
|
data/lib/elastic_apm/config.rb
CHANGED
@@ -17,9 +17,10 @@
|
|
17
17
|
|
18
18
|
# frozen_string_literal: true
|
19
19
|
|
20
|
-
require 'elastic_apm/config/options'
|
21
|
-
require 'elastic_apm/config/duration'
|
22
20
|
require 'elastic_apm/config/bytes'
|
21
|
+
require 'elastic_apm/config/duration'
|
22
|
+
require 'elastic_apm/config/options'
|
23
|
+
require 'elastic_apm/config/round_float'
|
23
24
|
require 'elastic_apm/config/regexp_list'
|
24
25
|
require 'elastic_apm/config/wildcard_pattern_list'
|
25
26
|
|
@@ -30,6 +31,11 @@ module ElasticAPM
|
|
30
31
|
|
31
32
|
DEPRECATED_OPTIONS = %i[].freeze
|
32
33
|
|
34
|
+
# DEPRECATED: To align with other agents, change on next major bump to:
|
35
|
+
# "password, passwd, pwd, secret, *key, *token*, *session*, *credit*, *card*, authorization, set-cookie"
|
36
|
+
SANITIZE_FIELD_NAMES_DEFAULT =
|
37
|
+
%w[*password* *passwd* *pwd* *secret* *key* *token* *session* *credit* *card* *authorization* *set-cookie*]
|
38
|
+
|
33
39
|
# rubocop:disable Metrics/LineLength, Layout/ExtraSpacing
|
34
40
|
option :config_file, type: :string, default: 'config/elastic_apm.yml'
|
35
41
|
option :server_url, type: :url, default: 'http://localhost:8200'
|
@@ -42,8 +48,10 @@ module ElasticAPM
|
|
42
48
|
option :breakdown_metrics, type: :bool, default: true
|
43
49
|
option :capture_body, type: :string, default: 'off'
|
44
50
|
option :capture_headers, type: :bool, default: true
|
51
|
+
option :capture_elasticsearch_queries, type: :bool, default: false
|
45
52
|
option :capture_env, type: :bool, default: true
|
46
53
|
option :central_config, type: :bool, default: true
|
54
|
+
option :cloud_provider, type: :string, default: 'auto'
|
47
55
|
option :current_user_email_method, type: :string, default: 'email'
|
48
56
|
option :current_user_id_method, type: :string, default: 'id'
|
49
57
|
option :current_user_username_method, type: :string, default: 'username'
|
@@ -75,9 +83,11 @@ module ElasticAPM
|
|
75
83
|
option :proxy_port, type: :int
|
76
84
|
option :proxy_username, type: :string
|
77
85
|
option :recording, type: :bool, default: true
|
78
|
-
option :sanitize_field_names, type: :list,
|
86
|
+
option :sanitize_field_names, type: :list,
|
87
|
+
default: SANITIZE_FIELD_NAMES_DEFAULT, converter: WildcardPatternList.new
|
79
88
|
option :server_ca_cert, type: :string
|
80
89
|
option :service_name, type: :string
|
90
|
+
option :service_node_name, type: :string
|
81
91
|
option :service_version, type: :string
|
82
92
|
option :source_lines_error_app_frames, type: :int, default: 5
|
83
93
|
option :source_lines_error_library_frames, type: :int, default: 0
|
@@ -85,8 +95,9 @@ module ElasticAPM
|
|
85
95
|
option :source_lines_span_library_frames, type: :int, default: 0
|
86
96
|
option :span_frames_min_duration, type: :float, default: '5ms', converter: Duration.new(default_unit: 'ms')
|
87
97
|
option :stack_trace_limit, type: :int, default: 999_999
|
98
|
+
option :transaction_ignore_urls, type: :list, default: [], converter: WildcardPatternList.new
|
88
99
|
option :transaction_max_spans, type: :int, default: 500
|
89
|
-
option :transaction_sample_rate, type: :float, default: 1.0
|
100
|
+
option :transaction_sample_rate, type: :float, default: 1.0, converter: RoundFloat.new
|
90
101
|
option :use_elastic_traceparent_header, type: :bool, default: true
|
91
102
|
option :use_legacy_sql_parser, type: :bool, default: false
|
92
103
|
option :verify_server_cert, type: :bool, default: true
|
@@ -129,6 +140,7 @@ module ElasticAPM
|
|
129
140
|
%w[
|
130
141
|
action_dispatch
|
131
142
|
delayed_job
|
143
|
+
dynamo_db
|
132
144
|
elasticsearch
|
133
145
|
faraday
|
134
146
|
http
|
@@ -158,13 +170,12 @@ module ElasticAPM
|
|
158
170
|
end
|
159
171
|
|
160
172
|
def replace_options(new_options)
|
161
|
-
return
|
173
|
+
return if new_options.nil? || new_options.empty?
|
162
174
|
options_copy = @options.dup
|
163
175
|
new_options.each do |key, value|
|
164
176
|
options_copy.fetch(key.to_sym).set(value)
|
165
177
|
end
|
166
178
|
@options = options_copy
|
167
|
-
set_log_level(logger)
|
168
179
|
end
|
169
180
|
|
170
181
|
def app=(app)
|
@@ -186,6 +197,14 @@ module ElasticAPM
|
|
186
197
|
metrics_interval > 0
|
187
198
|
end
|
188
199
|
|
200
|
+
# DEPRECATED: Remove this in next major version
|
201
|
+
def sanitize_field_names=(value)
|
202
|
+
list = WildcardPatternList.new.call(value)
|
203
|
+
defaults = WildcardPatternList.new.call(SANITIZE_FIELD_NAMES_DEFAULT)
|
204
|
+
get(:sanitize_field_names).value =
|
205
|
+
defaults.concat(list).uniq(&:pattern) # use regex pattern for comparisons
|
206
|
+
end
|
207
|
+
|
189
208
|
def span_frames_min_duration?
|
190
209
|
span_frames_min_duration != 0
|
191
210
|
end
|
@@ -232,6 +251,15 @@ module ElasticAPM
|
|
232
251
|
self.default_labels = value
|
233
252
|
end
|
234
253
|
|
254
|
+
def ignore_url_patterns=(value)
|
255
|
+
unless value == self.class.schema[:ignore_url_patterns][:default]
|
256
|
+
warn '[DEPRECATED] The option ignore_url_patterns is being removed. ' \
|
257
|
+
'Consider using transaction_ignore_urls instead.'
|
258
|
+
end
|
259
|
+
|
260
|
+
set(:ignore_url_patterns, value)
|
261
|
+
end
|
262
|
+
|
235
263
|
def custom_key_filters=(value)
|
236
264
|
unless value == self.class.schema[:custom_key_filters][:default]
|
237
265
|
warn '[DEPRECATED] The option custom_key_filters is being removed. ' \
|
@@ -286,15 +314,10 @@ module ElasticAPM
|
|
286
314
|
|
287
315
|
def build_logger
|
288
316
|
Logger.new(log_path == '-' ? STDOUT : log_path).tap do |logger|
|
289
|
-
|
317
|
+
logger.level = log_level
|
290
318
|
end
|
291
319
|
end
|
292
320
|
|
293
|
-
def set_log_level(logger)
|
294
|
-
return unless logger
|
295
|
-
logger.level = log_level
|
296
|
-
end
|
297
|
-
|
298
321
|
def app_type?(app)
|
299
322
|
if defined?(::Rails::Application) && app.is_a?(::Rails::Application)
|
300
323
|
return :rails
|
@@ -321,7 +344,7 @@ module ElasticAPM
|
|
321
344
|
self.logger ||= ::Rails.logger
|
322
345
|
|
323
346
|
self.__root_path = ::Rails.root.to_s
|
324
|
-
self.__view_paths = app.config.paths['app/views'].existent
|
347
|
+
self.__view_paths = app.config.paths['app/views'].existent + [::Rails.root.to_s]
|
325
348
|
end
|
326
349
|
|
327
350
|
def rails_app_name(app)
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# Licensed to Elasticsearch B.V. under one or more contributor
|
2
|
+
# license agreements. See the NOTICE file distributed with
|
3
|
+
# this work for additional information regarding copyright
|
4
|
+
# ownership. Elasticsearch B.V. licenses this file to you under
|
5
|
+
# the Apache License, Version 2.0 (the "License"); you may
|
6
|
+
# not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
12
|
+
# software distributed under the License is distributed on an
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
14
|
+
# KIND, either express or implied. See the License for the
|
15
|
+
# specific language governing permissions and limitations
|
16
|
+
# under the License.
|
17
|
+
|
18
|
+
# frozen_string_literal: true
|
19
|
+
|
20
|
+
require 'elastic_apm/util/precision_validator'
|
21
|
+
|
22
|
+
module ElasticAPM
|
23
|
+
class Config
|
24
|
+
# @api private
|
25
|
+
class RoundFloat
|
26
|
+
def call(value)
|
27
|
+
Util::PrecisionValidator.validate(value, precision: 4, minimum: 0.0001)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -27,6 +27,8 @@ module ElasticAPM
|
|
27
27
|
@pattern = convert(str)
|
28
28
|
end
|
29
29
|
|
30
|
+
attr_reader :pattern
|
31
|
+
|
30
32
|
def match?(other)
|
31
33
|
!!@pattern.match(other)
|
32
34
|
end
|
@@ -36,12 +38,22 @@ module ElasticAPM
|
|
36
38
|
private
|
37
39
|
|
38
40
|
def convert(str)
|
41
|
+
case_sensitive = false
|
42
|
+
|
43
|
+
if str.start_with?('(?-i)')
|
44
|
+
str = str.gsub(/^\(\?-\i\)/, '')
|
45
|
+
case_sensitive = true
|
46
|
+
end
|
47
|
+
|
39
48
|
parts =
|
40
49
|
str.chars.each_with_object([]) do |char, arr|
|
41
50
|
arr << (char == '*' ? '.*' : Regexp.escape(char))
|
42
51
|
end
|
43
52
|
|
44
|
-
Regexp.new(
|
53
|
+
Regexp.new(
|
54
|
+
'\A' + parts.join + '\Z',
|
55
|
+
case_sensitive ? nil : Regexp::IGNORECASE
|
56
|
+
)
|
45
57
|
end
|
46
58
|
end
|
47
59
|
|
data/lib/elastic_apm/grpc.rb
CHANGED
@@ -71,7 +71,7 @@ module ElasticAPM
|
|
71
71
|
transaction.done 'success'
|
72
72
|
rescue ::Exception => e
|
73
73
|
ElasticAPM.report(e, handled: false)
|
74
|
-
transaction.done 'error'
|
74
|
+
transaction.done 'error' if transaction
|
75
75
|
raise
|
76
76
|
ensure
|
77
77
|
ElasticAPM.end_transaction
|
@@ -91,7 +91,7 @@ module ElasticAPM
|
|
91
91
|
def trace_context(call)
|
92
92
|
TraceContext.parse(metadata: call.metadata)
|
93
93
|
rescue TraceContext::InvalidTraceparentHeader
|
94
|
-
warn "Couldn't parse invalid trace context header: #{
|
94
|
+
warn "Couldn't parse invalid trace context header: #{call.metadata}"
|
95
95
|
nil
|
96
96
|
end
|
97
97
|
end
|
@@ -119,7 +119,13 @@ module ElasticAPM
|
|
119
119
|
"Already inside #{transaction.inspect}"
|
120
120
|
end
|
121
121
|
|
122
|
-
|
122
|
+
if trace_context
|
123
|
+
sampled = trace_context.recorded?
|
124
|
+
sample_rate = trace_context.tracestate.sample_rate
|
125
|
+
else
|
126
|
+
sampled = random_sample?(config)
|
127
|
+
sample_rate = config.transaction_sample_rate
|
128
|
+
end
|
123
129
|
|
124
130
|
transaction =
|
125
131
|
Transaction.new(
|
@@ -128,6 +134,7 @@ module ElasticAPM
|
|
128
134
|
context: context,
|
129
135
|
trace_context: trace_context,
|
130
136
|
sampled: sampled,
|
137
|
+
sample_rate: sample_rate,
|
131
138
|
config: config
|
132
139
|
)
|
133
140
|
|
@@ -259,7 +266,7 @@ module ElasticAPM
|
|
259
266
|
end
|
260
267
|
|
261
268
|
def update_transaction_metrics(transaction)
|
262
|
-
return unless transaction.collect_metrics
|
269
|
+
return unless transaction.collect_metrics?
|
263
270
|
|
264
271
|
tags = {
|
265
272
|
'transaction.name': transaction.name,
|
@@ -298,7 +305,7 @@ module ElasticAPM
|
|
298
305
|
end
|
299
306
|
|
300
307
|
def update_span_metrics(span)
|
301
|
-
return unless span.transaction.
|
308
|
+
return unless span.transaction.collect_metrics?
|
302
309
|
|
303
310
|
tags = {
|
304
311
|
'span.type': span.type,
|
data/lib/elastic_apm/metadata.rb
CHANGED
@@ -25,12 +25,14 @@ module ElasticAPM
|
|
25
25
|
@process = ProcessInfo.new(config)
|
26
26
|
@system = SystemInfo.new(config)
|
27
27
|
@labels = config.global_labels
|
28
|
+
@cloud = CloudInfo.new(config).fetch!
|
28
29
|
end
|
29
30
|
|
30
|
-
attr_reader :service, :process, :system, :labels
|
31
|
+
attr_reader :service, :process, :system, :cloud, :labels
|
31
32
|
end
|
32
33
|
end
|
33
34
|
|
34
35
|
require 'elastic_apm/metadata/service_info'
|
35
36
|
require 'elastic_apm/metadata/system_info'
|
36
37
|
require 'elastic_apm/metadata/process_info'
|
38
|
+
require 'elastic_apm/metadata/cloud_info'
|
@@ -0,0 +1,128 @@
|
|
1
|
+
# Licensed to Elasticsearch B.V. under one or more contributor
|
2
|
+
# license agreements. See the NOTICE file distributed with
|
3
|
+
# this work for additional information regarding copyright
|
4
|
+
# ownership. Elasticsearch B.V. licenses this file to you under
|
5
|
+
# the Apache License, Version 2.0 (the "License"); you may
|
6
|
+
# not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing,
|
12
|
+
# software distributed under the License is distributed on an
|
13
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
14
|
+
# KIND, either express or implied. See the License for the
|
15
|
+
# specific language governing permissions and limitations
|
16
|
+
# under the License.
|
17
|
+
|
18
|
+
# frozen_string_literal: true
|
19
|
+
|
20
|
+
require "http"
|
21
|
+
|
22
|
+
module ElasticAPM
|
23
|
+
class Metadata
|
24
|
+
# @api private
|
25
|
+
class CloudInfo
|
26
|
+
include Logging
|
27
|
+
|
28
|
+
AWS_URI = "http://169.254.169.254/latest/dynamic/instance-identity/document"
|
29
|
+
GCP_URI = "http://metadata.google.internal/computeMetadata/v1/?recursive=true"
|
30
|
+
AZURE_URI = "http://169.254.169.254/metadata/instance/compute?api-version=2019-08-15"
|
31
|
+
|
32
|
+
def initialize(config)
|
33
|
+
@config = config
|
34
|
+
@client = HTTP.timeout(0.1)
|
35
|
+
end
|
36
|
+
|
37
|
+
attr_reader :config
|
38
|
+
|
39
|
+
attr_accessor(
|
40
|
+
:account_id,
|
41
|
+
:account_name,
|
42
|
+
:instance_id,
|
43
|
+
:instance_name,
|
44
|
+
:machine_type,
|
45
|
+
:project_id,
|
46
|
+
:project_name,
|
47
|
+
:availability_zone,
|
48
|
+
:provider,
|
49
|
+
:region
|
50
|
+
)
|
51
|
+
|
52
|
+
def fetch!
|
53
|
+
case config.cloud_provider
|
54
|
+
when "aws"
|
55
|
+
fetch_aws
|
56
|
+
when "gcp"
|
57
|
+
fetch_gcp
|
58
|
+
when "azure"
|
59
|
+
fetch_azure
|
60
|
+
when "auto"
|
61
|
+
fetch_aws || fetch_gcp || fetch_azure
|
62
|
+
when "none"
|
63
|
+
nil
|
64
|
+
else
|
65
|
+
error("Unknown setting for cloud_provider '#{config.cloud_provider}'")
|
66
|
+
end
|
67
|
+
|
68
|
+
self
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def fetch_aws
|
74
|
+
resp = @client.get(AWS_URI)
|
75
|
+
|
76
|
+
return unless resp.status === 200
|
77
|
+
return unless (metadata = JSON.parse(resp.body))
|
78
|
+
|
79
|
+
self.provider = "aws"
|
80
|
+
self.account_id = metadata["accountId"]
|
81
|
+
self.instance_id = metadata["instanceId"]
|
82
|
+
self.availability_zone = metadata["availabilityZone"]
|
83
|
+
self.machine_type = metadata["instanceType"]
|
84
|
+
self.region = metadata["region"]
|
85
|
+
rescue HTTP::TimeoutError, HTTP::ConnectionError
|
86
|
+
nil
|
87
|
+
end
|
88
|
+
|
89
|
+
def fetch_gcp
|
90
|
+
resp = @client.headers("Metadata-Flavor" => "Google").get(GCP_URI)
|
91
|
+
|
92
|
+
return unless resp.status === 200
|
93
|
+
return unless (metadata = JSON.parse(resp.body))
|
94
|
+
|
95
|
+
zone = metadata["instance"]["zone"]&.split("/")&.at(-1)
|
96
|
+
|
97
|
+
self.provider = "gcp"
|
98
|
+
self.instance_id = metadata["instance"]["id"]
|
99
|
+
self.instance_name = metadata["instance"]["name"]
|
100
|
+
self.project_id = metadata["project"]["numericProjectId"]
|
101
|
+
self.project_name = metadata["project"]["projectId"]
|
102
|
+
self.availability_zone = zone
|
103
|
+
self.region = zone.split("-")[0..-2].join("-")
|
104
|
+
self.machine_type = metadata["instance"]["machineType"]
|
105
|
+
rescue HTTP::TimeoutError, HTTP::ConnectionError
|
106
|
+
nil
|
107
|
+
end
|
108
|
+
|
109
|
+
def fetch_azure
|
110
|
+
resp = @client.headers("Metadata" => "true").get(AZURE_URI)
|
111
|
+
|
112
|
+
return unless resp.status === 200
|
113
|
+
return unless (metadata = JSON.parse(resp.body))
|
114
|
+
|
115
|
+
self.provider = 'azure'
|
116
|
+
self.account_id = metadata["subscriptionId"]
|
117
|
+
self.instance_id = metadata["vmId"]
|
118
|
+
self.instance_name = metadata["name"]
|
119
|
+
self.project_name = metadata["resourceGroupName"]
|
120
|
+
self.availability_zone = metadata["zone"]
|
121
|
+
self.machine_type = metadata["vmSize"]
|
122
|
+
self.region = metadata["location"]
|
123
|
+
rescue HTTP::TimeoutError, HTTP::ConnectionError
|
124
|
+
nil
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|