hoss-agent 1.0.9
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 +7 -0
- data/.github/ISSUE_TEMPLATE/Bug_report.md +40 -0
- data/.github/ISSUE_TEMPLATE/Feature_request.md +17 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +60 -0
- data/.gitignore +27 -0
- data/.rspec +2 -0
- data/Dockerfile +43 -0
- data/Gemfile +105 -0
- data/LICENSE +201 -0
- data/hoss-agent.gemspec +42 -0
- data/lib/hoss-agent.rb +210 -0
- data/lib/hoss.rb +21 -0
- data/lib/hoss/agent.rb +235 -0
- data/lib/hoss/central_config.rb +184 -0
- data/lib/hoss/central_config/cache_control.rb +51 -0
- data/lib/hoss/child_durations.rb +64 -0
- data/lib/hoss/config.rb +315 -0
- data/lib/hoss/config/bytes.rb +42 -0
- data/lib/hoss/config/duration.rb +40 -0
- data/lib/hoss/config/options.rb +154 -0
- data/lib/hoss/config/regexp_list.rb +30 -0
- data/lib/hoss/config/wildcard_pattern_list.rb +54 -0
- data/lib/hoss/context.rb +64 -0
- data/lib/hoss/context/request.rb +28 -0
- data/lib/hoss/context/request/socket.rb +36 -0
- data/lib/hoss/context/request/url.rb +59 -0
- data/lib/hoss/context/response.rb +47 -0
- data/lib/hoss/context/user.rb +59 -0
- data/lib/hoss/context_builder.rb +112 -0
- data/lib/hoss/deprecations.rb +39 -0
- data/lib/hoss/error.rb +49 -0
- data/lib/hoss/error/exception.rb +70 -0
- data/lib/hoss/error/log.rb +41 -0
- data/lib/hoss/error_builder.rb +90 -0
- data/lib/hoss/event.rb +131 -0
- data/lib/hoss/instrumenter.rb +107 -0
- data/lib/hoss/internal_error.rb +23 -0
- data/lib/hoss/logging.rb +70 -0
- data/lib/hoss/metadata.rb +36 -0
- data/lib/hoss/metadata/process_info.rb +35 -0
- data/lib/hoss/metadata/service_info.rb +76 -0
- data/lib/hoss/metadata/system_info.rb +47 -0
- data/lib/hoss/metadata/system_info/container_info.rb +136 -0
- data/lib/hoss/naively_hashable.rb +38 -0
- data/lib/hoss/rails.rb +68 -0
- data/lib/hoss/railtie.rb +42 -0
- data/lib/hoss/report.rb +9 -0
- data/lib/hoss/sinatra.rb +53 -0
- data/lib/hoss/spies.rb +104 -0
- data/lib/hoss/spies/faraday.rb +106 -0
- data/lib/hoss/spies/http.rb +86 -0
- data/lib/hoss/spies/net_http.rb +101 -0
- data/lib/hoss/stacktrace.rb +33 -0
- data/lib/hoss/stacktrace/frame.rb +66 -0
- data/lib/hoss/stacktrace_builder.rb +124 -0
- data/lib/hoss/transport/base.rb +191 -0
- data/lib/hoss/transport/connection.rb +55 -0
- data/lib/hoss/transport/connection/http.rb +139 -0
- data/lib/hoss/transport/connection/proxy_pipe.rb +94 -0
- data/lib/hoss/transport/filters.rb +60 -0
- data/lib/hoss/transport/filters/hash_sanitizer.rb +77 -0
- data/lib/hoss/transport/filters/secrets_filter.rb +48 -0
- data/lib/hoss/transport/headers.rb +74 -0
- data/lib/hoss/transport/serializers.rb +113 -0
- data/lib/hoss/transport/serializers/context_serializer.rb +112 -0
- data/lib/hoss/transport/serializers/error_serializer.rb +92 -0
- data/lib/hoss/transport/serializers/event_serializer.rb +73 -0
- data/lib/hoss/transport/serializers/metadata_serializer.rb +92 -0
- data/lib/hoss/transport/serializers/report_serializer.rb +33 -0
- data/lib/hoss/transport/user_agent.rb +48 -0
- data/lib/hoss/transport/worker.rb +330 -0
- data/lib/hoss/util.rb +54 -0
- data/lib/hoss/util/inflector.rb +110 -0
- data/lib/hoss/util/lru_cache.rb +65 -0
- data/lib/hoss/util/throttle.rb +52 -0
- data/lib/hoss/version.rb +22 -0
- metadata +147 -0
@@ -0,0 +1,184 @@
|
|
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 'hoss/central_config/cache_control'
|
21
|
+
|
22
|
+
module Hoss
|
23
|
+
# @api private
|
24
|
+
class CentralConfig
|
25
|
+
include Logging
|
26
|
+
|
27
|
+
# @api private
|
28
|
+
class ResponseError < InternalError
|
29
|
+
def initialize(response)
|
30
|
+
@response = response
|
31
|
+
end
|
32
|
+
|
33
|
+
attr_reader :response
|
34
|
+
end
|
35
|
+
class ClientError < ResponseError; end
|
36
|
+
class ServerError < ResponseError; end
|
37
|
+
|
38
|
+
def initialize(config)
|
39
|
+
@config = config
|
40
|
+
@modified_options = {}
|
41
|
+
@authorization = "Bearer #{@config.api_key}"
|
42
|
+
@http = Transport::Connection::Http.new(config)
|
43
|
+
@etag = 1
|
44
|
+
end
|
45
|
+
|
46
|
+
attr_reader :config
|
47
|
+
attr_reader :scheduled_task, :promise # for specs
|
48
|
+
|
49
|
+
def start
|
50
|
+
return unless config.central_config?
|
51
|
+
|
52
|
+
debug 'Starting CentralConfig'
|
53
|
+
|
54
|
+
fetch_and_apply_config
|
55
|
+
end
|
56
|
+
|
57
|
+
def stop
|
58
|
+
debug 'Stopping CentralConfig'
|
59
|
+
|
60
|
+
@scheduled_task&.cancel
|
61
|
+
end
|
62
|
+
|
63
|
+
def fetch_and_apply_config
|
64
|
+
@promise =
|
65
|
+
Concurrent::Promise
|
66
|
+
.execute(&method(:fetch_config))
|
67
|
+
.on_success(&method(:handle_success))
|
68
|
+
.rescue(&method(:handle_error))
|
69
|
+
end
|
70
|
+
|
71
|
+
def fetch_config
|
72
|
+
resp = perform_request
|
73
|
+
case resp.status
|
74
|
+
when 200..299
|
75
|
+
resp
|
76
|
+
when 300..399
|
77
|
+
resp
|
78
|
+
when 400..499
|
79
|
+
resp
|
80
|
+
# raise ClientError, resp
|
81
|
+
when 500..599
|
82
|
+
resp
|
83
|
+
# raise ServerError, resp
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def assign(update)
|
88
|
+
# For each updated option, store the original value,
|
89
|
+
# unless already stored
|
90
|
+
update.each_key do |key|
|
91
|
+
@modified_options[key] ||= config.get(key.to_sym)&.value
|
92
|
+
end
|
93
|
+
|
94
|
+
# If the new update doesn't set a previously modified option,
|
95
|
+
# revert it to the original
|
96
|
+
@modified_options.each_key do |key|
|
97
|
+
next if update.key?(key)
|
98
|
+
update[key] = @modified_options.delete(key)
|
99
|
+
end
|
100
|
+
@config.replace_options(update)
|
101
|
+
end
|
102
|
+
|
103
|
+
def handle_forking!
|
104
|
+
stop
|
105
|
+
start
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
111
|
+
def handle_success(resp)
|
112
|
+
if (etag = resp.headers['Etag'])
|
113
|
+
@etag = etag
|
114
|
+
end
|
115
|
+
|
116
|
+
if resp.status == 304
|
117
|
+
info 'Received 304 Not Modified'
|
118
|
+
else
|
119
|
+
if resp.body && !resp.body.empty?
|
120
|
+
update = JSON.parse(resp.body.to_s)
|
121
|
+
assign(update['data']) unless update.nil? or update['data'].nil?
|
122
|
+
end
|
123
|
+
|
124
|
+
if update && update.any?
|
125
|
+
info 'Updated config'
|
126
|
+
debug 'Modified: %s', update.inspect
|
127
|
+
debug 'Modified original options: %s', @modified_options.inspect
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
schedule_next_fetch(resp)
|
132
|
+
|
133
|
+
true
|
134
|
+
rescue Exception => e
|
135
|
+
error 'Failed to apply remote config, %s', e.inspect
|
136
|
+
debug e.backtrace.join('\n')
|
137
|
+
end
|
138
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
139
|
+
|
140
|
+
def handle_error(error)
|
141
|
+
puts error.backtrace
|
142
|
+
# For tests, WebMock failures don't have real responses
|
143
|
+
response = error.response if error.respond_to?(:response)
|
144
|
+
|
145
|
+
debug(
|
146
|
+
'Failed fetching config: %s, trying again in %d seconds',
|
147
|
+
response&.body, @config.remote_config_fetch_interval
|
148
|
+
)
|
149
|
+
|
150
|
+
assign({})
|
151
|
+
|
152
|
+
schedule_next_fetch(response)
|
153
|
+
end
|
154
|
+
|
155
|
+
def perform_request
|
156
|
+
body = '{"query":"query AgentConfig {\n agentConfig {\n accountApiConfiguration {\n uuid\n hostBlacklist\n sanitizedHeaders\n sanitizedQueryParams\n sanitizedBodyFields {\n type\n value\n }\n bodyCapture\n }\n apis {\n uuid\n name\n rootDomain\n hosts\n configuration(mergeWithAccountConfiguration: true) {\n uuid\n sanitizedHeaders\n sanitizedQueryParams\n bodyCapture\n sanitizedBodyFields {\n type\n value\n }\n }\n }\n accountRestrictions {\n ingressDisabled\n }\n }\n}","operationName":"AgentConfig"}'
|
157
|
+
@http.post(api_host, body: body, headers: headers)
|
158
|
+
end
|
159
|
+
|
160
|
+
def api_host
|
161
|
+
@api_host ||=
|
162
|
+
config.api_host +
|
163
|
+
'/api/graphql'
|
164
|
+
end
|
165
|
+
|
166
|
+
def headers
|
167
|
+
{ 'Etag': @etag, 'Authorization': @authorization, 'HOSS-SKIP-INSTRUMENTATION': true }
|
168
|
+
end
|
169
|
+
|
170
|
+
def schedule_next_fetch(resp = nil)
|
171
|
+
headers = resp&.headers
|
172
|
+
seconds =
|
173
|
+
if headers && headers['Cache-Control']
|
174
|
+
CacheControl.new(headers['Cache-Control']).max_age
|
175
|
+
else
|
176
|
+
@config.remote_config_fetch_interval
|
177
|
+
end
|
178
|
+
|
179
|
+
@scheduled_task =
|
180
|
+
Concurrent::ScheduledTask
|
181
|
+
.execute(seconds, &method(:fetch_and_apply_config))
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
@@ -0,0 +1,51 @@
|
|
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
|
+
module Hoss
|
21
|
+
class CentralConfig
|
22
|
+
# @api private
|
23
|
+
class CacheControl
|
24
|
+
def initialize(value)
|
25
|
+
@header = value
|
26
|
+
parse!(value)
|
27
|
+
end
|
28
|
+
|
29
|
+
attr_reader(
|
30
|
+
:must_revalidate,
|
31
|
+
:no_cache,
|
32
|
+
:no_store,
|
33
|
+
:no_transform,
|
34
|
+
:public,
|
35
|
+
:private,
|
36
|
+
:proxy_revalidate,
|
37
|
+
:max_age,
|
38
|
+
:s_maxage
|
39
|
+
)
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def parse!(value)
|
44
|
+
value.split(',').each do |token|
|
45
|
+
k, v = token.split('=').map(&:strip)
|
46
|
+
instance_variable_set(:"@#{k.tr('-', '_')}", v ? v.to_i : true)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,64 @@
|
|
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
|
+
module Hoss
|
21
|
+
# @api private
|
22
|
+
module ChildDurations
|
23
|
+
# @api private
|
24
|
+
module Methods
|
25
|
+
def child_durations
|
26
|
+
@child_durations ||= Durations.new
|
27
|
+
end
|
28
|
+
|
29
|
+
def child_started
|
30
|
+
child_durations.start
|
31
|
+
end
|
32
|
+
|
33
|
+
def child_stopped
|
34
|
+
child_durations.stop
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# @api private
|
39
|
+
class Durations
|
40
|
+
def initialize
|
41
|
+
@nesting_level = 0
|
42
|
+
@start = nil
|
43
|
+
@duration = 0
|
44
|
+
@mutex = Mutex.new
|
45
|
+
end
|
46
|
+
|
47
|
+
attr_reader :duration
|
48
|
+
|
49
|
+
def start
|
50
|
+
@mutex.synchronize do
|
51
|
+
@nesting_level += 1
|
52
|
+
@start = Util.micros if @nesting_level == 1
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def stop
|
57
|
+
@mutex.synchronize do
|
58
|
+
@nesting_level -= 1
|
59
|
+
@duration = (Util.micros - @start) if @nesting_level == 0
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/hoss/config.rb
ADDED
@@ -0,0 +1,315 @@
|
|
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 'hoss/config/options'
|
21
|
+
require 'hoss/config/duration'
|
22
|
+
require 'hoss/config/bytes'
|
23
|
+
require 'hoss/config/regexp_list'
|
24
|
+
require 'hoss/config/wildcard_pattern_list'
|
25
|
+
|
26
|
+
module Hoss
|
27
|
+
# @api private
|
28
|
+
class Config
|
29
|
+
extend Options
|
30
|
+
|
31
|
+
DEPRECATED_OPTIONS = %i[].freeze
|
32
|
+
|
33
|
+
# rubocop:disable Metrics/LineLength, Layout/ExtraSpacing
|
34
|
+
option :config_file, type: :string, default: 'config/hoss.yml'
|
35
|
+
option :api_host, type: :url, default: ENV['HOSS_API_HOST'] || 'https://app.hoss.com'
|
36
|
+
option :api_key, type: :string, default: ENV['HOSS_API_KEY']
|
37
|
+
option :ingress_host, type: :string, default: ENV['HOSS_INGRESS_HOST'] || 'https://ingress.hoss.com/v1'
|
38
|
+
option :max_queue_size, type: :int, default: 3000
|
39
|
+
option :batch_size, type: :int, default: ENV['HOSS_BATCH_SIZE'] || 512000
|
40
|
+
option :max_event_retries, type: :int, default: 100
|
41
|
+
option :remote_config_fetch_interval, type: :int, default: 300
|
42
|
+
option :agentConfig, type: :dict
|
43
|
+
option :debug, type: :boolean, default: ENV['HOSS_DEBUG']
|
44
|
+
option :disable_reporting, type: :boolean, default: ENV['HOSS_DISABLE_REPORTING'] == 'true'
|
45
|
+
|
46
|
+
option :central_config, type: :bool, default: true
|
47
|
+
option :capture_body, type: :string, default: 'off'
|
48
|
+
option :capture_headers, type: :bool, default: true
|
49
|
+
option :capture_env, type: :bool, default: true
|
50
|
+
option :current_user_email_method, type: :string, default: 'email'
|
51
|
+
option :current_user_id_method, type: :string, default: 'id'
|
52
|
+
option :current_user_username_method, type: :string, default: 'username'
|
53
|
+
option :custom_key_filters, type: :list, default: [], converter: RegexpList.new
|
54
|
+
option :default_labels, type: :dict, default: {}
|
55
|
+
option :disable_start_message, type: :bool, default: false
|
56
|
+
option :disable_instrumentations, type: :list, default: %w[]
|
57
|
+
option :enabled, type: :bool, default: true
|
58
|
+
option :environment, type: :string, default: ENV['RAILS_ENV'] || ENV['RACK_ENV']
|
59
|
+
option :framework_name, type: :string
|
60
|
+
option :framework_version, type: :string
|
61
|
+
option :filter_exception_types, type: :list, default: []
|
62
|
+
option :global_labels, type: :dict
|
63
|
+
option :hostname, type: :string
|
64
|
+
option :http_compression, type: :bool, default: true
|
65
|
+
option :instrument, type: :bool, default: true
|
66
|
+
option :log_level, type: :int, default: Logger::WARN
|
67
|
+
option :log_path, type: :string
|
68
|
+
option :pool_size, type: :int, default: 1
|
69
|
+
option :proxy_address, type: :string
|
70
|
+
option :proxy_headers, type: :dict
|
71
|
+
option :proxy_password, type: :string
|
72
|
+
option :proxy_port, type: :int
|
73
|
+
option :proxy_username, type: :string
|
74
|
+
option :recording, type: :bool, default: true
|
75
|
+
option :sanitize_field_names, type: :list, default: [], converter: WildcardPatternList.new
|
76
|
+
option :server_ca_cert, type: :string
|
77
|
+
option :service_name, type: :string
|
78
|
+
option :service_node_name, type: :string
|
79
|
+
option :service_version, type: :string
|
80
|
+
option :span_frames_min_duration, type: :float, default: '5ms', converter: Duration.new(default_unit: 'ms')
|
81
|
+
option :stack_trace_limit, type: :int, default: 999_999
|
82
|
+
option :verify_server_cert, type: :bool, default: true
|
83
|
+
|
84
|
+
# rubocop:enable Metrics/LineLength, Layout/ExtraSpacing
|
85
|
+
def initialize(options = {})
|
86
|
+
@options = load_schema
|
87
|
+
|
88
|
+
if options.is_a? String
|
89
|
+
assign(api_key: options)
|
90
|
+
else
|
91
|
+
assign(options)
|
92
|
+
end
|
93
|
+
|
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
|
99
|
+
end
|
100
|
+
|
101
|
+
assign(load_config_file)
|
102
|
+
assign(env)
|
103
|
+
|
104
|
+
if self.debug == true || self.debug == 'true'
|
105
|
+
assign(
|
106
|
+
log_level: Logger::DEBUG,
|
107
|
+
debug: true,
|
108
|
+
log_path: '-'
|
109
|
+
)
|
110
|
+
end
|
111
|
+
|
112
|
+
unless self.disable_reporting.nil?
|
113
|
+
self.disable_reporting = self.disable_reporting == 'true' || self.disable_reporting == true
|
114
|
+
end
|
115
|
+
|
116
|
+
yield self if block_given?
|
117
|
+
|
118
|
+
self.logger ||= build_logger
|
119
|
+
|
120
|
+
@__view_paths ||= []
|
121
|
+
@__root_path ||= Dir.pwd
|
122
|
+
end
|
123
|
+
|
124
|
+
attr_accessor :__view_paths, :__root_path
|
125
|
+
attr_accessor :logger
|
126
|
+
|
127
|
+
attr_reader :options
|
128
|
+
|
129
|
+
def assign(update)
|
130
|
+
return unless update
|
131
|
+
update.each { |key, value| send(:"#{key}=", value) }
|
132
|
+
end
|
133
|
+
|
134
|
+
def available_instrumentations
|
135
|
+
%w[
|
136
|
+
http
|
137
|
+
net_http
|
138
|
+
faraday
|
139
|
+
]
|
140
|
+
end
|
141
|
+
|
142
|
+
def enabled_instrumentations
|
143
|
+
available_instrumentations - disable_instrumentations
|
144
|
+
end
|
145
|
+
|
146
|
+
def method_missing(name, *args)
|
147
|
+
return super unless DEPRECATED_OPTIONS.include?(name)
|
148
|
+
warn "The option `#{name}' has been removed."
|
149
|
+
end
|
150
|
+
|
151
|
+
def replace_options(new_options)
|
152
|
+
return if new_options.nil? || new_options.empty?
|
153
|
+
options_copy = @options.dup
|
154
|
+
new_options.each do |key, value|
|
155
|
+
options_copy.fetch(key.to_sym).set(value)
|
156
|
+
end
|
157
|
+
@options = options_copy
|
158
|
+
end
|
159
|
+
|
160
|
+
def app=(app)
|
161
|
+
case app_type?(app)
|
162
|
+
when :sinatra
|
163
|
+
set_sinatra(app)
|
164
|
+
when :rails
|
165
|
+
set_rails(app)
|
166
|
+
else
|
167
|
+
self.service_name = 'ruby'
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def use_ssl?
|
172
|
+
api_host.start_with?('https')
|
173
|
+
end
|
174
|
+
|
175
|
+
def span_frames_min_duration?
|
176
|
+
span_frames_min_duration != 0
|
177
|
+
end
|
178
|
+
|
179
|
+
def span_frames_min_duration=(value)
|
180
|
+
super
|
181
|
+
@span_frames_min_duration_us = nil
|
182
|
+
end
|
183
|
+
|
184
|
+
def span_frames_min_duration_us
|
185
|
+
@span_frames_min_duration_us ||= span_frames_min_duration * 1_000_000
|
186
|
+
end
|
187
|
+
|
188
|
+
def ssl_context
|
189
|
+
return unless use_ssl?
|
190
|
+
|
191
|
+
@ssl_context ||=
|
192
|
+
OpenSSL::SSL::SSLContext.new.tap do |context|
|
193
|
+
if server_ca_cert
|
194
|
+
context.ca_file = server_ca_cert
|
195
|
+
else
|
196
|
+
context.cert_store =
|
197
|
+
OpenSSL::X509::Store.new.tap(&:set_default_paths)
|
198
|
+
end
|
199
|
+
|
200
|
+
context.verify_mode =
|
201
|
+
if verify_server_cert
|
202
|
+
OpenSSL::SSL::VERIFY_PEER
|
203
|
+
else
|
204
|
+
OpenSSL::SSL::VERIFY_NONE
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def inspect
|
210
|
+
super.split.first + '>'
|
211
|
+
end
|
212
|
+
|
213
|
+
# Deprecations
|
214
|
+
|
215
|
+
def default_tags=(value)
|
216
|
+
warn '[DEPRECATED] The option default_tags has been renamed to ' \
|
217
|
+
'default_labels.'
|
218
|
+
self.default_labels = value
|
219
|
+
end
|
220
|
+
|
221
|
+
def custom_key_filters=(value)
|
222
|
+
unless value == self.class.schema[:custom_key_filters][:default]
|
223
|
+
warn '[DEPRECATED] The option custom_key_filters is being removed. ' \
|
224
|
+
'See sanitize_field_names for an alternative.'
|
225
|
+
end
|
226
|
+
|
227
|
+
set(:custom_key_filters, value)
|
228
|
+
end
|
229
|
+
|
230
|
+
def disabled_instrumentations
|
231
|
+
disable_instrumentations
|
232
|
+
end
|
233
|
+
|
234
|
+
def active
|
235
|
+
enabled
|
236
|
+
end
|
237
|
+
alias active? active
|
238
|
+
|
239
|
+
def disabled_instrumentations=(value)
|
240
|
+
warn '[DEPRECATED] The option disabled_instrumentations has been ' \
|
241
|
+
'renamed to disable_instrumentations to align with other agents.'
|
242
|
+
self.disable_instrumentations = value
|
243
|
+
end
|
244
|
+
|
245
|
+
def active=(value)
|
246
|
+
warn '[DEPRECATED] The option active has been renamed to enabled ' \
|
247
|
+
'to align with other agents and with the remote config.'
|
248
|
+
self.enabled = value
|
249
|
+
end
|
250
|
+
|
251
|
+
private
|
252
|
+
|
253
|
+
def load_config_file
|
254
|
+
filename = File.join(@__root_path || Dir.pwd, config_file)
|
255
|
+
return unless File.exist?(filename)
|
256
|
+
read = File.read(filename)
|
257
|
+
evaled = ERB.new(read).result
|
258
|
+
YAML.safe_load(evaled)
|
259
|
+
end
|
260
|
+
|
261
|
+
def load_env
|
262
|
+
@options.values.each_with_object({}) do |option, opts|
|
263
|
+
next unless (value = ENV[option.env_key])
|
264
|
+
opts[option.key] = value
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
def build_logger
|
269
|
+
Logger.new(log_path == '-' ? STDOUT : log_path).tap do |logger|
|
270
|
+
logger.level = log_level
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
def app_type?(app)
|
275
|
+
if defined?(::Rails::Application) && app.is_a?(::Rails::Application)
|
276
|
+
return :rails
|
277
|
+
end
|
278
|
+
|
279
|
+
if app.is_a?(Class) && app.superclass.to_s == 'Sinatra::Base'
|
280
|
+
return :sinatra
|
281
|
+
end
|
282
|
+
|
283
|
+
nil
|
284
|
+
end
|
285
|
+
|
286
|
+
def set_sinatra(app)
|
287
|
+
self.service_name = format_name(service_name || app.to_s)
|
288
|
+
self.framework_name = framework_name || 'Sinatra'
|
289
|
+
self.framework_version = framework_version || ::Sinatra::VERSION
|
290
|
+
self.__root_path = Dir.pwd
|
291
|
+
end
|
292
|
+
|
293
|
+
def set_rails(app)
|
294
|
+
self.service_name ||= format_name(service_name || rails_app_name(app))
|
295
|
+
self.framework_name ||= 'Ruby on Rails'
|
296
|
+
self.framework_version ||= ::Rails::VERSION::STRING
|
297
|
+
self.logger ||= ::Rails.logger
|
298
|
+
|
299
|
+
self.__root_path = ::Rails.root.to_s
|
300
|
+
self.__view_paths = app.config.paths['app/views'].existent
|
301
|
+
end
|
302
|
+
|
303
|
+
def rails_app_name(app)
|
304
|
+
if ::Rails::VERSION::MAJOR >= 6
|
305
|
+
app.class.module_parent_name
|
306
|
+
else
|
307
|
+
app.class.parent_name
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
def format_name(str)
|
312
|
+
str&.gsub('::', '_')
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|