elastic-apm 2.8.1 → 2.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/.jenkins_codecov.yml +5 -0
- data/.ci/.jenkins_exclude.yml +63 -0
- data/.ci/.jenkins_framework.yml +9 -0
- data/.ci/.jenkins_master_framework.yml +3 -0
- data/.ci/.jenkins_ruby.yml +11 -0
- data/.ci/Jenkinsfile +268 -0
- data/.ci/bin/check_paths_for_matches.py +80 -0
- data/.ci/downstreamTests.groovy +188 -0
- data/.ci/jobs/apm-agent-ruby-downstream.yml +37 -0
- data/.ci/jobs/apm-agent-ruby-linting-mbp.yml +38 -0
- data/.ci/jobs/apm-agent-ruby-mbp.yml +41 -0
- data/.ci/jobs/apm-agent-ruby.yml +4 -0
- data/.ci/jobs/defaults.yml +24 -0
- data/.ci/linting.groovy +32 -0
- data/.ci/prepare-git-context.sh +23 -0
- data/.pre-commit-config.yaml +22 -0
- data/.rspec +0 -1
- data/.rubocop.yml +3 -3
- data/CHANGELOG.md +59 -2
- data/docs/api.asciidoc +24 -7
- data/docs/configuration.asciidoc +43 -4
- data/docs/index.asciidoc +2 -0
- data/docs/log-correlation.asciidoc +96 -0
- data/docs/metrics.asciidoc +77 -6
- data/lib/elastic_apm.rb +37 -5
- data/lib/elastic_apm/agent.rb +29 -4
- data/lib/elastic_apm/central_config.rb +141 -0
- data/lib/elastic_apm/central_config/cache_control.rb +34 -0
- data/lib/elastic_apm/config.rb +165 -340
- data/lib/elastic_apm/config/bytes.rb +25 -0
- data/lib/elastic_apm/config/duration.rb +6 -8
- data/lib/elastic_apm/config/options.rb +134 -0
- data/lib/elastic_apm/config/regexp_list.rb +13 -0
- data/lib/elastic_apm/context_builder.rb +2 -0
- data/lib/elastic_apm/error/exception.rb +3 -1
- data/lib/elastic_apm/error_builder.rb +6 -3
- data/lib/elastic_apm/instrumenter.rb +6 -0
- data/lib/elastic_apm/metadata.rb +2 -1
- data/lib/elastic_apm/metrics.rb +2 -1
- data/lib/elastic_apm/metrics/vm.rb +60 -0
- data/lib/elastic_apm/normalizers/action_controller.rb +5 -2
- data/lib/elastic_apm/normalizers/action_mailer.rb +5 -2
- data/lib/elastic_apm/normalizers/action_view.rb +14 -9
- data/lib/elastic_apm/normalizers/active_record.rb +5 -2
- data/lib/elastic_apm/rails.rb +59 -0
- data/lib/elastic_apm/railtie.rb +11 -48
- data/lib/elastic_apm/span.rb +29 -7
- data/lib/elastic_apm/spies/faraday.rb +9 -2
- data/lib/elastic_apm/spies/http.rb +9 -2
- data/lib/elastic_apm/spies/mongo.rb +18 -3
- data/lib/elastic_apm/spies/net_http.rb +8 -2
- data/lib/elastic_apm/stacktrace/frame.rb +3 -1
- data/lib/elastic_apm/stacktrace_builder.rb +2 -2
- data/lib/elastic_apm/subscriber.rb +11 -3
- data/lib/elastic_apm/transport/connection.rb +17 -3
- data/lib/elastic_apm/transport/connection/proxy_pipe.rb +8 -1
- data/lib/elastic_apm/transport/filters/secrets_filter.rb +3 -1
- data/lib/elastic_apm/transport/serializers/error_serializer.rb +12 -2
- data/lib/elastic_apm/transport/serializers/metadata_serializer.rb +6 -1
- data/lib/elastic_apm/transport/serializers/span_serializer.rb +11 -3
- data/lib/elastic_apm/version.rb +1 -1
- metadata +26 -4
- data/Jenkinsfile +0 -280
- data/lib/elastic_apm/config/size.rb +0 -28
data/lib/elastic_apm/agent.rb
CHANGED
@@ -1,13 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'elastic_apm/error'
|
4
|
+
|
3
5
|
require 'elastic_apm/context_builder'
|
4
6
|
require 'elastic_apm/error_builder'
|
5
7
|
require 'elastic_apm/stacktrace_builder'
|
6
|
-
|
8
|
+
|
9
|
+
require 'elastic_apm/central_config'
|
7
10
|
require 'elastic_apm/transport/base'
|
8
|
-
require 'elastic_apm/spies'
|
9
11
|
require 'elastic_apm/metrics'
|
10
12
|
|
13
|
+
require 'elastic_apm/spies'
|
14
|
+
|
11
15
|
module ElasticAPM
|
12
16
|
# rubocop:disable Metrics/ClassLength
|
13
17
|
# @api private
|
@@ -57,6 +61,7 @@ module ElasticAPM
|
|
57
61
|
!!@instance
|
58
62
|
end
|
59
63
|
|
64
|
+
# rubocop:disable Metrics/MethodLength
|
60
65
|
def initialize(config)
|
61
66
|
@config = config
|
62
67
|
|
@@ -64,6 +69,7 @@ module ElasticAPM
|
|
64
69
|
@context_builder = ContextBuilder.new(config)
|
65
70
|
@error_builder = ErrorBuilder.new(self)
|
66
71
|
|
72
|
+
@central_config = CentralConfig.new(config)
|
67
73
|
@transport = Transport::Base.new(config)
|
68
74
|
@instrumenter = Instrumenter.new(
|
69
75
|
config,
|
@@ -71,9 +77,18 @@ module ElasticAPM
|
|
71
77
|
) { |event| enqueue event }
|
72
78
|
@metrics = Metrics.new(config) { |event| enqueue event }
|
73
79
|
end
|
80
|
+
# rubocop:enable Metrics/MethodLength
|
74
81
|
|
75
|
-
attr_reader
|
76
|
-
:
|
82
|
+
attr_reader(
|
83
|
+
:central_config,
|
84
|
+
:config,
|
85
|
+
:context_builder,
|
86
|
+
:error_builder,
|
87
|
+
:instrumenter,
|
88
|
+
:metrics,
|
89
|
+
:stacktrace_builder,
|
90
|
+
:transport
|
91
|
+
)
|
77
92
|
|
78
93
|
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
79
94
|
def start
|
@@ -81,6 +96,7 @@ module ElasticAPM
|
|
81
96
|
info '[%s] Starting agent, reporting to %s', VERSION, config.server_url
|
82
97
|
end
|
83
98
|
|
99
|
+
central_config.start
|
84
100
|
transport.start
|
85
101
|
instrumenter.start
|
86
102
|
metrics.start
|
@@ -97,6 +113,7 @@ module ElasticAPM
|
|
97
113
|
def stop
|
98
114
|
debug 'Stopping agent'
|
99
115
|
|
116
|
+
central_config.stop
|
100
117
|
metrics.stop
|
101
118
|
instrumenter.stop
|
102
119
|
transport.stop
|
@@ -142,9 +159,12 @@ module ElasticAPM
|
|
142
159
|
instrumenter.end_transaction(result)
|
143
160
|
end
|
144
161
|
|
162
|
+
# rubocop:disable Metrics/ParameterLists
|
145
163
|
def start_span(
|
146
164
|
name = nil,
|
147
165
|
type = nil,
|
166
|
+
subtype: nil,
|
167
|
+
action: nil,
|
148
168
|
backtrace: nil,
|
149
169
|
context: nil,
|
150
170
|
trace_context: nil
|
@@ -152,11 +172,14 @@ module ElasticAPM
|
|
152
172
|
instrumenter.start_span(
|
153
173
|
name,
|
154
174
|
type,
|
175
|
+
subtype: subtype,
|
176
|
+
action: action,
|
155
177
|
backtrace: backtrace,
|
156
178
|
context: context,
|
157
179
|
trace_context: trace_context
|
158
180
|
)
|
159
181
|
end
|
182
|
+
# rubocop:enable Metrics/ParameterLists
|
160
183
|
|
161
184
|
def end_span
|
162
185
|
instrumenter.end_span
|
@@ -189,6 +212,7 @@ module ElasticAPM
|
|
189
212
|
handled: handled
|
190
213
|
)
|
191
214
|
enqueue error
|
215
|
+
error.id
|
192
216
|
end
|
193
217
|
|
194
218
|
def report_message(message, context: nil, backtrace: nil, **attrs)
|
@@ -199,6 +223,7 @@ module ElasticAPM
|
|
199
223
|
**attrs
|
200
224
|
)
|
201
225
|
enqueue error
|
226
|
+
error.id
|
202
227
|
end
|
203
228
|
|
204
229
|
# filters
|
@@ -0,0 +1,141 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'elastic_apm/central_config/cache_control'
|
4
|
+
|
5
|
+
module ElasticAPM
|
6
|
+
# @api private
|
7
|
+
class CentralConfig
|
8
|
+
include Logging
|
9
|
+
|
10
|
+
# @api private
|
11
|
+
class ResponseError < InternalError
|
12
|
+
def initialize(response)
|
13
|
+
@response = response
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :response
|
17
|
+
end
|
18
|
+
class ClientError < ResponseError; end
|
19
|
+
class ServerError < ResponseError; end
|
20
|
+
|
21
|
+
DEFAULT_MAX_AGE = 300
|
22
|
+
|
23
|
+
def initialize(config)
|
24
|
+
@config = config
|
25
|
+
@modified_options = {}
|
26
|
+
@service_info = {
|
27
|
+
'service.name': config.service_name,
|
28
|
+
'service.environment': config.environment
|
29
|
+
}.to_json
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_reader :config
|
33
|
+
attr_reader :scheduled_task, :promise # for specs
|
34
|
+
|
35
|
+
def start
|
36
|
+
return unless config.central_config?
|
37
|
+
|
38
|
+
fetch_and_apply_config
|
39
|
+
end
|
40
|
+
|
41
|
+
def fetch_and_apply_config
|
42
|
+
@promise =
|
43
|
+
Concurrent::Promise
|
44
|
+
.execute(&method(:fetch_config))
|
45
|
+
.on_success(&method(:handle_success))
|
46
|
+
.rescue(&method(:handle_error))
|
47
|
+
end
|
48
|
+
|
49
|
+
def stop
|
50
|
+
@scheduled_task&.cancel
|
51
|
+
end
|
52
|
+
|
53
|
+
# rubocop:disable Metrics/MethodLength
|
54
|
+
def fetch_config
|
55
|
+
resp = perform_request
|
56
|
+
|
57
|
+
case resp.status
|
58
|
+
when 200..299
|
59
|
+
resp
|
60
|
+
when 300..399
|
61
|
+
resp
|
62
|
+
when 400..499
|
63
|
+
raise ClientError, resp
|
64
|
+
when 500..599
|
65
|
+
raise ServerError, resp
|
66
|
+
end
|
67
|
+
end
|
68
|
+
# rubocop:enable Metrics/MethodLength
|
69
|
+
|
70
|
+
def assign(update)
|
71
|
+
# For each updated option, store the original value,
|
72
|
+
# unless already stored
|
73
|
+
update.each_key do |key|
|
74
|
+
@modified_options[key] ||= config.get(key.to_sym)&.value
|
75
|
+
end
|
76
|
+
|
77
|
+
# If the new update doesn't set a previously modified option,
|
78
|
+
# revert it to the original
|
79
|
+
@modified_options.each_key do |key|
|
80
|
+
next if update.key?(key)
|
81
|
+
update[key] = @modified_options.delete(key)
|
82
|
+
end
|
83
|
+
|
84
|
+
config.assign(update)
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
# rubocop:disable Metrics/MethodLength
|
90
|
+
def handle_success(resp)
|
91
|
+
if resp.status == 304
|
92
|
+
info 'Received 304 Not Modified'
|
93
|
+
else
|
94
|
+
update = JSON.parse(resp.body.to_s)
|
95
|
+
assign(update)
|
96
|
+
|
97
|
+
info 'Updated config from Kibana'
|
98
|
+
end
|
99
|
+
|
100
|
+
schedule_next_fetch(resp)
|
101
|
+
|
102
|
+
true
|
103
|
+
rescue Exception => e
|
104
|
+
error 'Failed to apply remote config, %s', e.inspect
|
105
|
+
debug { e.backtrace.join('\n') }
|
106
|
+
end
|
107
|
+
# rubocop:enable Metrics/MethodLength
|
108
|
+
|
109
|
+
def handle_error(error)
|
110
|
+
error(
|
111
|
+
'Failed fetching config: %s, trying again in %d seconds',
|
112
|
+
error.response.body, DEFAULT_MAX_AGE
|
113
|
+
)
|
114
|
+
|
115
|
+
assign({})
|
116
|
+
|
117
|
+
schedule_next_fetch(error.response)
|
118
|
+
end
|
119
|
+
|
120
|
+
def perform_request
|
121
|
+
Http.post(
|
122
|
+
config.server_url + '/agent/v1/config/',
|
123
|
+
body: @service_info,
|
124
|
+
headers: { etag: 1, content_type: 'application/json' }
|
125
|
+
)
|
126
|
+
end
|
127
|
+
|
128
|
+
def schedule_next_fetch(resp)
|
129
|
+
seconds =
|
130
|
+
if (cache_header = resp.headers['Cache-Control'])
|
131
|
+
CacheControl.new(cache_header).max_age
|
132
|
+
else
|
133
|
+
DEFAULT_MAX_AGE
|
134
|
+
end
|
135
|
+
|
136
|
+
@scheduled_task =
|
137
|
+
Concurrent::ScheduledTask
|
138
|
+
.execute(seconds, &method(:fetch_and_apply_config))
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ElasticAPM
|
4
|
+
class CentralConfig
|
5
|
+
# @api private
|
6
|
+
class CacheControl
|
7
|
+
def initialize(value)
|
8
|
+
@header = value
|
9
|
+
parse!(value)
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader(
|
13
|
+
:must_revalidate,
|
14
|
+
:no_cache,
|
15
|
+
:no_store,
|
16
|
+
:no_transform,
|
17
|
+
:public,
|
18
|
+
:private,
|
19
|
+
:proxy_revalidate,
|
20
|
+
:max_age,
|
21
|
+
:s_maxage
|
22
|
+
)
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def parse!(value)
|
27
|
+
value.split(',').each do |token|
|
28
|
+
k, v = token.split('=').map(&:strip)
|
29
|
+
instance_variable_set(:"@#{k.gsub('-', '_')}", v ? v.to_i : true)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/elastic_apm/config.rb
CHANGED
@@ -5,246 +5,119 @@ require 'yaml'
|
|
5
5
|
require 'erb'
|
6
6
|
|
7
7
|
require 'elastic_apm/util/prefixed_logger'
|
8
|
+
|
9
|
+
require 'elastic_apm/config/options'
|
8
10
|
require 'elastic_apm/config/duration'
|
9
|
-
require 'elastic_apm/config/
|
11
|
+
require 'elastic_apm/config/bytes'
|
12
|
+
require 'elastic_apm/config/regexp_list'
|
10
13
|
|
11
14
|
module ElasticAPM
|
12
|
-
class ConfigError < StandardError; end
|
13
|
-
|
14
15
|
# rubocop:disable Metrics/ClassLength
|
15
16
|
# @api private
|
16
17
|
class Config
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
current_user_email_method: :email,
|
30
|
-
current_user_id_method: :id,
|
31
|
-
current_user_username_method: :username,
|
32
|
-
custom_key_filters: [],
|
33
|
-
default_tags: {},
|
34
|
-
disable_send: false,
|
35
|
-
disable_start_message: false,
|
36
|
-
disabled_spies: %w[json],
|
37
|
-
environment: ENV['RAILS_ENV'] || ENV['RACK_ENV'],
|
38
|
-
filter_exception_types: [],
|
39
|
-
http_compression: true,
|
40
|
-
ignore_url_patterns: [],
|
41
|
-
instrument: true,
|
42
|
-
instrumented_rake_tasks: [],
|
43
|
-
log_level: Logger::INFO,
|
44
|
-
log_path: nil,
|
45
|
-
metrics_interval: '30s',
|
46
|
-
pool_size: 1,
|
47
|
-
source_lines_error_app_frames: 5,
|
48
|
-
source_lines_error_library_frames: 0,
|
49
|
-
source_lines_span_app_frames: 5,
|
50
|
-
source_lines_span_library_frames: 0,
|
51
|
-
span_frames_min_duration: '5ms',
|
52
|
-
stack_trace_limit: 999_999,
|
53
|
-
transaction_max_spans: 500,
|
54
|
-
transaction_sample_rate: 1.0,
|
55
|
-
verify_server_cert: true,
|
56
|
-
|
57
|
-
view_paths: [],
|
58
|
-
root_path: Dir.pwd
|
59
|
-
}.freeze
|
60
|
-
|
61
|
-
ENV_TO_KEY = {
|
62
|
-
'ELASTIC_APM_SERVER_URL' => 'server_url',
|
63
|
-
'ELASTIC_APM_SECRET_TOKEN' => 'secret_token',
|
64
|
-
|
65
|
-
'ELASTIC_APM_ACTIVE' => [:bool, 'active'],
|
66
|
-
'ELASTIC_APM_API_BUFFER_SIZE' => [:int, 'api_buffer_size'],
|
67
|
-
'ELASTIC_APM_API_REQUEST_SIZE' => [:int, 'api_request_size'],
|
68
|
-
'ELASTIC_APM_API_REQUEST_TIME' => 'api_request_time',
|
69
|
-
'ELASTIC_APM_CAPTURE_BODY' => 'capture_body',
|
70
|
-
'ELASTIC_APM_CAPTURE_HEADERS' => [:bool, 'capture_headers'],
|
71
|
-
'ELASTIC_APM_CAPTURE_ENV' => [:bool, 'capture_env'],
|
72
|
-
'ELASTIC_APM_CONFIG_FILE' => 'config_file',
|
73
|
-
'ELASTIC_APM_CUSTOM_KEY_FILTERS' => [:list, 'custom_key_filters'],
|
74
|
-
'ELASTIC_APM_DEFAULT_TAGS' => [:dict, 'default_tags'],
|
75
|
-
'ELASTIC_APM_DISABLED_SPIES' => [:list, 'disabled_spies'],
|
76
|
-
'ELASTIC_APM_DISABLE_SEND' => [:bool, 'disable_send'],
|
77
|
-
'ELASTIC_APM_DISABLE_START_MESSAGE' => [:bool, 'disable_start_message'],
|
78
|
-
'ELASTIC_APM_ENVIRONMENT' => 'environment',
|
79
|
-
'ELASTIC_APM_FRAMEWORK_NAME' => 'framework_name',
|
80
|
-
'ELASTIC_APM_FRAMEWORK_VERSION' => 'framework_version',
|
81
|
-
'ELASTIC_APM_HOSTNAME' => 'hostname',
|
82
|
-
'ELASTIC_APM_IGNORE_URL_PATTERNS' => [:list, 'ignore_url_patterns'],
|
83
|
-
'ELASTIC_APM_INSTRUMENT' => [:bool, 'instrument'],
|
84
|
-
'ELASTIC_APM_INSTRUMENTED_RAKE_TASKS' =>
|
85
|
-
[:list, 'instrumented_rake_tasks'],
|
86
|
-
'ELASTIC_APM_LOG_LEVEL' => [:int, 'log_level'],
|
87
|
-
'ELASTIC_APM_LOG_PATH' => 'log_path',
|
88
|
-
'ELASTIC_APM_METRICS_INTERVAL' => 'metrics_interval',
|
89
|
-
'ELASTIC_APM_PROXY_ADDRESS' => 'proxy_address',
|
90
|
-
'ELASTIC_APM_PROXY_HEADERS' => [:dict, 'proxy_headers'],
|
91
|
-
'ELASTIC_APM_PROXY_PASSWORD' => 'proxy_password',
|
92
|
-
'ELASTIC_APM_PROXY_PORT' => [:int, 'proxy_port'],
|
93
|
-
'ELASTIC_APM_PROXY_USERNAME' => 'proxy_username',
|
94
|
-
'ELASTIC_APM_POOL_SIZE' => [:int, 'pool_size'],
|
95
|
-
'ELASTIC_APM_SERVER_CA_CERT' => 'server_ca_cert',
|
96
|
-
'ELASTIC_APM_SERVICE_NAME' => 'service_name',
|
97
|
-
'ELASTIC_APM_SERVICE_VERSION' => 'service_version',
|
98
|
-
'ELASTIC_APM_SOURCE_LINES_ERROR_APP_FRAMES' =>
|
99
|
-
[:int, 'source_lines_error_app_frames'],
|
100
|
-
'ELASTIC_APM_SOURCE_LINES_ERROR_LIBRARY_FRAMES' =>
|
101
|
-
[:int, 'source_lines_error_library_frames'],
|
102
|
-
'ELASTIC_APM_SOURCE_LINES_SPAN_APP_FRAMES' =>
|
103
|
-
[:int, 'source_lines_span_app_frames'],
|
104
|
-
'ELASTIC_APM_SOURCE_LINES_SPAN_LIBRARY_FRAMES' =>
|
105
|
-
[:int, 'source_lines_span_library_frames'],
|
106
|
-
'ELASTIC_APM_SPAN_FRAMES_MIN_DURATION' => 'span_frames_min_duration',
|
107
|
-
'ELASTIC_APM_STACK_TRACE_LIMIT' => [:int, 'stack_trace_limit'],
|
108
|
-
'ELASTIC_APM_TRANSACTION_MAX_SPANS' => [:int, 'transaction_max_spans'],
|
109
|
-
'ELASTIC_APM_TRANSACTION_SAMPLE_RATE' =>
|
110
|
-
[:float, 'transaction_sample_rate'],
|
111
|
-
'ELASTIC_APM_VERIFY_SERVER_CERT' => [:bool, 'verify_server_cert']
|
112
|
-
}.freeze
|
113
|
-
|
114
|
-
DURATION_KEYS = %i[
|
115
|
-
api_request_time
|
116
|
-
span_frames_min_duration
|
117
|
-
metrics_interval
|
18
|
+
extend Options
|
19
|
+
|
20
|
+
DEPRECATED_OPTIONS = %i[
|
21
|
+
compression_level=
|
22
|
+
compression_minimum_size=
|
23
|
+
debug_http=
|
24
|
+
debug_transactions=
|
25
|
+
flush_interval=
|
26
|
+
http_open_timeout=
|
27
|
+
http_read_timeout=
|
28
|
+
enabled_environments=
|
29
|
+
disable_environment_warning=
|
118
30
|
].freeze
|
119
|
-
DURATION_DEFAULT_UNITS = { # default is 's'
|
120
|
-
span_frames_min_duration: 'ms'
|
121
|
-
}.freeze
|
122
31
|
|
123
|
-
|
124
|
-
|
32
|
+
# rubocop:disable Metrics/LineLength, Layout/ExtraSpacing
|
33
|
+
option :config_file, type: :string, default: 'config/elastic_apm.yml'
|
34
|
+
option :server_url, type: :string, default: 'http://localhost:8200'
|
35
|
+
option :secret_token, type: :string
|
36
|
+
|
37
|
+
option :active, type: :bool, default: true
|
38
|
+
option :api_buffer_size, type: :int, default: 256
|
39
|
+
option :api_request_size, type: :bytes, default: '750kb', converter: Bytes.new
|
40
|
+
option :api_request_time, type: :float, default: '10s', converter: Duration.new
|
41
|
+
option :capture_body, type: :string, default: 'off'
|
42
|
+
option :capture_headers, type: :bool, default: true
|
43
|
+
option :capture_env, type: :bool, default: true
|
44
|
+
option :central_config, type: :bool, default: true
|
45
|
+
option :current_user_email_method, type: :string, default: 'email'
|
46
|
+
option :current_user_id_method, type: :string, default: 'id'
|
47
|
+
option :current_user_username_method, type: :string, default: 'username'
|
48
|
+
option :custom_key_filters, type: :list, default: [], converter: RegexpList.new
|
49
|
+
option :default_tags, type: :dict, default: {}
|
50
|
+
option :disable_send, type: :bool, default: false
|
51
|
+
option :disable_start_message, type: :bool, default: false
|
52
|
+
option :disabled_spies, type: :list, default: %w[json]
|
53
|
+
option :environment, type: :string, default: ENV['RAILS_ENV'] || ENV['RACK_ENV']
|
54
|
+
option :framework_name, type: :string
|
55
|
+
option :framework_version, type: :string
|
56
|
+
option :filter_exception_types, type: :list, default: []
|
57
|
+
option :global_labels, type: :dict
|
58
|
+
option :hostname, type: :string
|
59
|
+
option :http_compression, type: :bool, default: true
|
60
|
+
option :ignore_url_patterns, type: :list, default: [], converter: RegexpList.new
|
61
|
+
option :instrument, type: :bool, default: true
|
62
|
+
option :instrumented_rake_tasks, type: :list, default: []
|
63
|
+
option :log_level, type: :int, default: Logger::INFO
|
64
|
+
option :log_path, type: :string
|
65
|
+
option :metrics_interval, type: :int, default: '30s', converter: Duration.new
|
66
|
+
option :pool_size, type: :int, default: 1
|
67
|
+
option :proxy_address, type: :string
|
68
|
+
option :proxy_headers, type: :dict
|
69
|
+
option :proxy_password, type: :string
|
70
|
+
option :proxy_port, type: :int
|
71
|
+
option :proxy_username, type: :string
|
72
|
+
option :server_ca_cert, type: :string
|
73
|
+
option :service_name, type: :string
|
74
|
+
option :service_version, type: :string
|
75
|
+
option :source_lines_error_app_frames, type: :int, default: 5
|
76
|
+
option :source_lines_error_library_frames, type: :int, default: 0
|
77
|
+
option :source_lines_span_app_frames, type: :int, default: 5
|
78
|
+
option :source_lines_span_library_frames, type: :int, default: 0
|
79
|
+
option :span_frames_min_duration, type: :float, default: '5ms', converter: Duration.new(default_unit: 'ms')
|
80
|
+
option :stack_trace_limit, type: :int, default: 999_999
|
81
|
+
option :transaction_max_spans, type: :int, default: 500
|
82
|
+
option :transaction_sample_rate, type: :float, default: 1.0
|
83
|
+
option :verify_server_cert, type: :bool, default: true
|
84
|
+
# rubocop:enable Metrics/LineLength, Layout/ExtraSpacing
|
125
85
|
|
86
|
+
# rubocop:disable Metrics/MethodLength
|
126
87
|
def initialize(options = {})
|
127
|
-
|
128
|
-
|
129
|
-
set_from_args(options)
|
130
|
-
set_from_config_file
|
131
|
-
set_from_env
|
132
|
-
|
133
|
-
normalize_durations
|
134
|
-
normalize_sizes
|
88
|
+
@options = load_schema
|
135
89
|
|
136
|
-
|
137
|
-
|
138
|
-
build_logger if logger.nil?
|
139
|
-
end
|
90
|
+
custom_logger = options.delete(:logger)
|
140
91
|
|
141
|
-
|
142
|
-
|
143
|
-
attr_accessor :server_url
|
144
|
-
attr_accessor :secret_token
|
145
|
-
|
146
|
-
attr_accessor :active
|
147
|
-
attr_accessor :api_buffer_size
|
148
|
-
attr_accessor :api_request_size
|
149
|
-
attr_accessor :api_request_time
|
150
|
-
attr_accessor :capture_env
|
151
|
-
attr_accessor :capture_headers
|
152
|
-
attr_accessor :current_user_email_method
|
153
|
-
attr_accessor :current_user_id_method
|
154
|
-
attr_accessor :current_user_method
|
155
|
-
attr_accessor :current_user_username_method
|
156
|
-
attr_accessor :default_tags
|
157
|
-
attr_accessor :disable_send
|
158
|
-
attr_accessor :disable_start_message
|
159
|
-
attr_accessor :disabled_spies
|
160
|
-
attr_accessor :environment
|
161
|
-
attr_accessor :filter_exception_types
|
162
|
-
attr_accessor :framework_name
|
163
|
-
attr_accessor :framework_version
|
164
|
-
attr_accessor :hostname
|
165
|
-
attr_accessor :http_compression
|
166
|
-
attr_accessor :instrument
|
167
|
-
attr_accessor :instrumented_rake_tasks
|
168
|
-
attr_accessor :log_level
|
169
|
-
attr_accessor :log_path
|
170
|
-
attr_accessor :logger
|
171
|
-
attr_accessor :metrics_interval
|
172
|
-
attr_accessor :pool_size
|
173
|
-
attr_accessor :proxy_address
|
174
|
-
attr_accessor :proxy_headers
|
175
|
-
attr_accessor :proxy_password
|
176
|
-
attr_accessor :proxy_port
|
177
|
-
attr_accessor :proxy_username
|
178
|
-
attr_accessor :server_ca_cert
|
179
|
-
attr_accessor :service_name
|
180
|
-
attr_accessor :service_version
|
181
|
-
attr_accessor :source_lines_error_app_frames
|
182
|
-
attr_accessor :source_lines_error_library_frames
|
183
|
-
attr_accessor :source_lines_span_app_frames
|
184
|
-
attr_accessor :source_lines_span_library_frames
|
185
|
-
attr_accessor :stack_trace_limit
|
186
|
-
attr_accessor :transaction_max_spans
|
187
|
-
attr_accessor :transaction_sample_rate
|
188
|
-
attr_accessor :verify_server_cert
|
189
|
-
|
190
|
-
attr_reader :capture_body
|
191
|
-
attr_reader :custom_key_filters
|
192
|
-
attr_reader :ignore_url_patterns
|
193
|
-
attr_reader :span_frames_min_duration
|
194
|
-
attr_reader :span_frames_min_duration_us
|
195
|
-
|
196
|
-
attr_writer :alert_logger
|
197
|
-
|
198
|
-
attr_accessor :view_paths
|
199
|
-
attr_accessor :root_path
|
200
|
-
|
201
|
-
alias :active? :active
|
202
|
-
alias :capture_body? :capture_body
|
203
|
-
alias :capture_headers? :capture_headers
|
204
|
-
alias :capture_env? :capture_env
|
205
|
-
alias :disable_send? :disable_send
|
206
|
-
alias :disable_start_message? :disable_start_message
|
207
|
-
alias :http_compression? :http_compression
|
208
|
-
alias :instrument? :instrument
|
209
|
-
alias :verify_server_cert? :verify_server_cert
|
210
|
-
|
211
|
-
def alert_logger
|
212
|
-
@alert_logger ||= PrefixedLogger.new($stdout, prefix: Logging::PREFIX)
|
213
|
-
end
|
92
|
+
assign(options)
|
214
93
|
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
set_rails(app)
|
221
|
-
else
|
222
|
-
self.service_name = 'ruby'
|
94
|
+
# Pick out config_file specifically as we need it now to load it,
|
95
|
+
# but still need the other env vars to have precedence
|
96
|
+
env = load_env
|
97
|
+
if (env_config_file = env.delete(:config_file))
|
98
|
+
self.config_file = env_config_file
|
223
99
|
end
|
224
|
-
end
|
225
100
|
|
226
|
-
|
227
|
-
|
228
|
-
return :rails
|
229
|
-
end
|
101
|
+
assign(load_config_file)
|
102
|
+
assign(env)
|
230
103
|
|
231
|
-
if
|
232
|
-
return :sinatra
|
233
|
-
end
|
104
|
+
yield self if block_given?
|
234
105
|
|
235
|
-
|
236
|
-
end
|
106
|
+
@logger = custom_logger || build_logger
|
237
107
|
|
238
|
-
|
239
|
-
|
108
|
+
@__view_paths = []
|
109
|
+
@__root_path = Dir.pwd
|
240
110
|
end
|
111
|
+
# rubocop:enable Metrics/MethodLength
|
241
112
|
|
242
|
-
|
243
|
-
|
244
|
-
|
113
|
+
attr_accessor :__view_paths, :__root_path
|
114
|
+
attr_accessor :logger
|
115
|
+
|
116
|
+
attr_reader :options
|
245
117
|
|
246
|
-
def
|
247
|
-
|
118
|
+
def assign(update)
|
119
|
+
return unless update
|
120
|
+
update.each { |key, value| send(:"#{key}=", value) }
|
248
121
|
end
|
249
122
|
|
250
123
|
# rubocop:disable Metrics/MethodLength
|
@@ -271,183 +144,135 @@ module ElasticAPM
|
|
271
144
|
available_spies - disabled_spies
|
272
145
|
end
|
273
146
|
|
274
|
-
def span_frames_min_duration=(duration)
|
275
|
-
@span_frames_min_duration = duration
|
276
|
-
@span_frames_min_duration_us = duration * 1_000_000
|
277
|
-
end
|
278
|
-
|
279
|
-
def span_frames_min_duration?
|
280
|
-
span_frames_min_duration != 0
|
281
|
-
end
|
282
|
-
|
283
|
-
DEPRECATED_OPTIONS = %i[
|
284
|
-
compression_level=
|
285
|
-
compression_minimum_size=
|
286
|
-
debug_http=
|
287
|
-
debug_transactions=
|
288
|
-
flush_interval=
|
289
|
-
http_open_timeout=
|
290
|
-
http_read_timeout=
|
291
|
-
enabled_environments=
|
292
|
-
disable_environment_warning=
|
293
|
-
].freeze
|
294
|
-
|
295
|
-
def respond_to_missing?(name)
|
296
|
-
return true if DEPRECATED_OPTIONS.include? name
|
297
|
-
return true if name.to_s.end_with?('=')
|
298
|
-
false
|
299
|
-
end
|
300
|
-
|
301
147
|
def method_missing(name, *args)
|
302
|
-
|
303
|
-
|
304
|
-
return
|
305
|
-
end
|
306
|
-
|
307
|
-
if name.to_s.end_with?('=')
|
308
|
-
raise ConfigError, "No such option `#{name.to_s.delete('=')}'"
|
309
|
-
end
|
310
|
-
|
311
|
-
super
|
148
|
+
return super unless DEPRECATED_OPTIONS.include?(name)
|
149
|
+
warn "The option `#{name}' has been removed."
|
312
150
|
end
|
313
151
|
|
314
|
-
def
|
315
|
-
|
152
|
+
def app=(app)
|
153
|
+
case app_type?(app)
|
154
|
+
when :sinatra
|
155
|
+
set_sinatra(app)
|
156
|
+
when :rails
|
157
|
+
set_rails(app)
|
158
|
+
else
|
159
|
+
self.service_name = 'ruby'
|
160
|
+
end
|
316
161
|
end
|
317
162
|
|
318
163
|
# rubocop:disable Metrics/MethodLength
|
319
164
|
def capture_body=(value)
|
320
165
|
if value =~ /(all|transactions|errors|off)/
|
321
|
-
|
166
|
+
set(:capture_body, value)
|
322
167
|
return
|
323
168
|
end
|
324
169
|
|
325
170
|
case value
|
326
171
|
when true
|
327
|
-
|
172
|
+
warn "Boolean value for option `capture_body' has " \
|
328
173
|
"been deprecated. Setting to 'all'"
|
329
|
-
|
174
|
+
self.capture_body = 'all'
|
330
175
|
when false
|
331
|
-
|
176
|
+
warn "Boolean value for option `capture_body' has " \
|
332
177
|
"been deprecated. Setting to 'off'"
|
333
|
-
|
178
|
+
self.capture_body = 'off'
|
334
179
|
else
|
335
|
-
default =
|
336
|
-
|
180
|
+
default = options[:capture_body].default
|
181
|
+
warn "Unknown value `#{value}' for option "\
|
337
182
|
"`capture_body'. Defaulting to `#{default}'"
|
338
|
-
|
183
|
+
self.capture_body = default
|
339
184
|
end
|
340
185
|
end
|
341
186
|
# rubocop:enable Metrics/MethodLength
|
342
187
|
|
343
|
-
|
188
|
+
def use_ssl?
|
189
|
+
server_url.start_with?('https')
|
190
|
+
end
|
344
191
|
|
345
|
-
def
|
346
|
-
|
347
|
-
send("#{key}=", value)
|
348
|
-
end
|
192
|
+
def collect_metrics?
|
193
|
+
metrics_interval > 0
|
349
194
|
end
|
350
195
|
|
351
|
-
def
|
352
|
-
|
196
|
+
def span_frames_min_duration?
|
197
|
+
span_frames_min_duration != 0
|
353
198
|
end
|
354
199
|
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
ENV_TO_KEY.each do |env_key, key|
|
359
|
-
next unless (value = ENV[env_key])
|
360
|
-
|
361
|
-
type, key = key if key.is_a? Array
|
362
|
-
|
363
|
-
value =
|
364
|
-
case type
|
365
|
-
when :int then value.to_i
|
366
|
-
when :float then value.to_f
|
367
|
-
when :bool then !%w[0 false].include?(value.strip.downcase)
|
368
|
-
when :list then value.split(/[ ,]/)
|
369
|
-
when :dict then Hash[value.split(/[&,]/).map { |kv| kv.split('=') }]
|
370
|
-
else value
|
371
|
-
end
|
372
|
-
|
373
|
-
send("#{key}=", value)
|
374
|
-
end
|
200
|
+
def span_frames_min_duration=(value)
|
201
|
+
super
|
202
|
+
@span_frames_min_duration_us = nil
|
375
203
|
end
|
376
|
-
# rubocop:enable Metrics/AbcSize
|
377
|
-
# rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity
|
378
204
|
|
379
|
-
def
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
)
|
205
|
+
def span_frames_min_duration_us
|
206
|
+
@span_frames_min_duration_us ||= span_frames_min_duration * 1_000_000
|
207
|
+
end
|
208
|
+
|
209
|
+
def inspect
|
210
|
+
super.split.first + '>'
|
386
211
|
end
|
387
212
|
|
388
|
-
|
213
|
+
private
|
214
|
+
|
215
|
+
def load_config_file
|
389
216
|
return unless File.exist?(config_file)
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
217
|
+
|
218
|
+
read = File.read(config_file)
|
219
|
+
evaled = ERB.new(read).result
|
220
|
+
YAML.safe_load(evaled)
|
221
|
+
end
|
222
|
+
|
223
|
+
def load_env
|
224
|
+
@options.values.each_with_object({}) do |option, opts|
|
225
|
+
next unless (value = ENV[option.env_key])
|
226
|
+
opts[option.key] = value
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def build_logger
|
231
|
+
Logger.new(log_path == '-' ? STDOUT : log_path).tap do |logger|
|
232
|
+
logger.level = log_level
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
def app_type?(app)
|
237
|
+
if defined?(::Rails::Application) && app.is_a?(::Rails::Application)
|
238
|
+
return :rails
|
239
|
+
end
|
240
|
+
|
241
|
+
if app.is_a?(Class) && app.superclass.to_s == 'Sinatra::Base'
|
242
|
+
return :sinatra
|
243
|
+
end
|
244
|
+
|
245
|
+
nil
|
396
246
|
end
|
397
247
|
|
398
248
|
def set_sinatra(app)
|
399
249
|
self.service_name = format_name(service_name || app.to_s)
|
400
250
|
self.framework_name = framework_name || 'Sinatra'
|
401
251
|
self.framework_version = framework_version || Sinatra::VERSION
|
402
|
-
self.
|
252
|
+
self.__root_path = Dir.pwd
|
403
253
|
end
|
404
254
|
|
405
255
|
def set_rails(app) # rubocop:disable Metrics/AbcSize
|
406
256
|
self.service_name ||= format_name(service_name || rails_app_name(app))
|
407
257
|
self.framework_name ||= 'Ruby on Rails'
|
408
|
-
self.framework_version ||= Rails::VERSION::STRING
|
409
|
-
self.logger ||= Rails.logger
|
258
|
+
self.framework_version ||= ::Rails::VERSION::STRING
|
259
|
+
self.logger ||= ::Rails.logger
|
410
260
|
|
411
|
-
self.
|
412
|
-
self.
|
261
|
+
self.__root_path = ::Rails.root.to_s
|
262
|
+
self.__view_paths = app.config.paths['app/views'].existent
|
413
263
|
end
|
414
264
|
|
415
265
|
def rails_app_name(app)
|
416
|
-
if Rails::VERSION::MAJOR >= 6
|
266
|
+
if ::Rails::VERSION::MAJOR >= 6
|
417
267
|
app.class.module_parent_name
|
418
268
|
else
|
419
269
|
app.class.parent_name
|
420
270
|
end
|
421
271
|
end
|
422
272
|
|
423
|
-
def build_logger
|
424
|
-
logger = Logger.new(log_path == '-' ? STDOUT : log_path)
|
425
|
-
logger.level = log_level
|
426
|
-
|
427
|
-
self.logger = logger
|
428
|
-
end
|
429
|
-
|
430
273
|
def format_name(str)
|
431
274
|
str && str.gsub('::', '_')
|
432
275
|
end
|
433
|
-
|
434
|
-
def normalize_durations
|
435
|
-
DURATION_KEYS.each do |key|
|
436
|
-
value = send(key).to_s
|
437
|
-
default_unit = DURATION_DEFAULT_UNITS.fetch(key, 's')
|
438
|
-
duration = Duration.parse(value, default_unit: default_unit)
|
439
|
-
send("#{key}=", duration.seconds)
|
440
|
-
end
|
441
|
-
end
|
442
|
-
|
443
|
-
def normalize_sizes
|
444
|
-
SIZE_KEYS.each do |key|
|
445
|
-
value = send(key).to_s
|
446
|
-
default_unit = SIZE_DEFAULT_UNITS.fetch(key, 'b')
|
447
|
-
size = Size.parse(value, default_unit: default_unit)
|
448
|
-
send("#{key}=", size.bytes)
|
449
|
-
end
|
450
|
-
end
|
451
276
|
end
|
452
277
|
# rubocop:enable Metrics/ClassLength
|
453
278
|
end
|