atatus 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/CHANGELOG.md +11 -0
  4. data/Gemfile +57 -0
  5. data/LICENSE +65 -0
  6. data/LICENSE-THIRD-PARTY +205 -0
  7. data/README.md +13 -0
  8. data/Rakefile +19 -0
  9. data/atatus.gemspec +36 -0
  10. data/atatus.yml +2 -0
  11. data/bench/.gitignore +2 -0
  12. data/bench/app.rb +53 -0
  13. data/bench/benchmark.rb +36 -0
  14. data/bench/report.rb +55 -0
  15. data/bench/rubyprof.rb +39 -0
  16. data/bench/stackprof.rb +23 -0
  17. data/bin/build_docs +5 -0
  18. data/bin/console +15 -0
  19. data/bin/setup +8 -0
  20. data/bin/with_framework +7 -0
  21. data/lib/atatus.rb +325 -0
  22. data/lib/atatus/agent.rb +260 -0
  23. data/lib/atatus/central_config.rb +141 -0
  24. data/lib/atatus/central_config/cache_control.rb +34 -0
  25. data/lib/atatus/collector/base.rb +329 -0
  26. data/lib/atatus/collector/builder.rb +317 -0
  27. data/lib/atatus/collector/transport.rb +72 -0
  28. data/lib/atatus/config.rb +248 -0
  29. data/lib/atatus/config/bytes.rb +25 -0
  30. data/lib/atatus/config/duration.rb +23 -0
  31. data/lib/atatus/config/options.rb +134 -0
  32. data/lib/atatus/config/regexp_list.rb +13 -0
  33. data/lib/atatus/context.rb +33 -0
  34. data/lib/atatus/context/request.rb +11 -0
  35. data/lib/atatus/context/request/socket.rb +19 -0
  36. data/lib/atatus/context/request/url.rb +42 -0
  37. data/lib/atatus/context/response.rb +22 -0
  38. data/lib/atatus/context/user.rb +42 -0
  39. data/lib/atatus/context_builder.rb +97 -0
  40. data/lib/atatus/deprecations.rb +22 -0
  41. data/lib/atatus/error.rb +22 -0
  42. data/lib/atatus/error/exception.rb +46 -0
  43. data/lib/atatus/error/log.rb +24 -0
  44. data/lib/atatus/error_builder.rb +76 -0
  45. data/lib/atatus/instrumenter.rb +224 -0
  46. data/lib/atatus/internal_error.rb +6 -0
  47. data/lib/atatus/logging.rb +55 -0
  48. data/lib/atatus/metadata.rb +19 -0
  49. data/lib/atatus/metadata/process_info.rb +18 -0
  50. data/lib/atatus/metadata/service_info.rb +61 -0
  51. data/lib/atatus/metadata/system_info.rb +35 -0
  52. data/lib/atatus/metadata/system_info/container_info.rb +121 -0
  53. data/lib/atatus/metadata/system_info/hw_info.rb +118 -0
  54. data/lib/atatus/metadata/system_info/os_info.rb +31 -0
  55. data/lib/atatus/metrics.rb +98 -0
  56. data/lib/atatus/metrics/cpu_mem.rb +240 -0
  57. data/lib/atatus/metrics/vm.rb +60 -0
  58. data/lib/atatus/metricset.rb +19 -0
  59. data/lib/atatus/middleware.rb +76 -0
  60. data/lib/atatus/naively_hashable.rb +21 -0
  61. data/lib/atatus/normalizers.rb +68 -0
  62. data/lib/atatus/normalizers/action_controller.rb +27 -0
  63. data/lib/atatus/normalizers/action_mailer.rb +26 -0
  64. data/lib/atatus/normalizers/action_view.rb +77 -0
  65. data/lib/atatus/normalizers/active_record.rb +45 -0
  66. data/lib/atatus/opentracing.rb +346 -0
  67. data/lib/atatus/rails.rb +61 -0
  68. data/lib/atatus/railtie.rb +30 -0
  69. data/lib/atatus/span.rb +125 -0
  70. data/lib/atatus/span/context.rb +40 -0
  71. data/lib/atatus/span_helpers.rb +44 -0
  72. data/lib/atatus/spies.rb +86 -0
  73. data/lib/atatus/spies/action_dispatch.rb +28 -0
  74. data/lib/atatus/spies/delayed_job.rb +68 -0
  75. data/lib/atatus/spies/elasticsearch.rb +36 -0
  76. data/lib/atatus/spies/faraday.rb +70 -0
  77. data/lib/atatus/spies/http.rb +44 -0
  78. data/lib/atatus/spies/json.rb +22 -0
  79. data/lib/atatus/spies/mongo.rb +87 -0
  80. data/lib/atatus/spies/net_http.rb +70 -0
  81. data/lib/atatus/spies/rake.rb +45 -0
  82. data/lib/atatus/spies/redis.rb +27 -0
  83. data/lib/atatus/spies/sequel.rb +47 -0
  84. data/lib/atatus/spies/sidekiq.rb +89 -0
  85. data/lib/atatus/spies/sinatra.rb +41 -0
  86. data/lib/atatus/spies/tilt.rb +27 -0
  87. data/lib/atatus/sql_summarizer.rb +35 -0
  88. data/lib/atatus/stacktrace.rb +16 -0
  89. data/lib/atatus/stacktrace/frame.rb +52 -0
  90. data/lib/atatus/stacktrace_builder.rb +104 -0
  91. data/lib/atatus/subscriber.rb +77 -0
  92. data/lib/atatus/trace_context.rb +85 -0
  93. data/lib/atatus/transaction.rb +100 -0
  94. data/lib/atatus/transport/base.rb +174 -0
  95. data/lib/atatus/transport/connection.rb +156 -0
  96. data/lib/atatus/transport/connection/http.rb +116 -0
  97. data/lib/atatus/transport/connection/proxy_pipe.rb +75 -0
  98. data/lib/atatus/transport/filters.rb +43 -0
  99. data/lib/atatus/transport/filters/secrets_filter.rb +74 -0
  100. data/lib/atatus/transport/serializers.rb +93 -0
  101. data/lib/atatus/transport/serializers/context_serializer.rb +85 -0
  102. data/lib/atatus/transport/serializers/error_serializer.rb +77 -0
  103. data/lib/atatus/transport/serializers/metadata_serializer.rb +70 -0
  104. data/lib/atatus/transport/serializers/metricset_serializer.rb +28 -0
  105. data/lib/atatus/transport/serializers/span_serializer.rb +80 -0
  106. data/lib/atatus/transport/serializers/transaction_serializer.rb +37 -0
  107. data/lib/atatus/transport/worker.rb +73 -0
  108. data/lib/atatus/util.rb +42 -0
  109. data/lib/atatus/util/inflector.rb +93 -0
  110. data/lib/atatus/util/lru_cache.rb +48 -0
  111. data/lib/atatus/util/prefixed_logger.rb +18 -0
  112. data/lib/atatus/util/throttle.rb +35 -0
  113. data/lib/atatus/version.rb +5 -0
  114. data/vendor/.gitkeep +0 -0
  115. metadata +190 -0
@@ -0,0 +1,317 @@
1
+ require 'socket'
2
+ require 'atatus/metadata'
3
+
4
+ module Atatus
5
+ class Builder
6
+ AGENT_NAME = 'Ruby'
7
+
8
+ def initialize(config)
9
+ @config = config
10
+ @metadata = Metadata::SystemInfo.new(config)
11
+ end
12
+
13
+ attr_reader :config
14
+
15
+ def common()
16
+ common = {
17
+ appName: @config.app_name,
18
+ licenseKey: @config.license_key,
19
+ agent: {
20
+ name: AGENT_NAME,
21
+ version: VERSION
22
+ },
23
+ # tags: @config.default_tags,
24
+ uniqueHostname: Socket.gethostname,
25
+ hostname: @metadata.hostname,
26
+ hostId: @metadata.hwinfo.hostid
27
+ }
28
+ unless @metadata.container.nil?
29
+ common[:containerId] = @metadata.container[:id] if @metadata.container.key?(:id)
30
+ end
31
+ common
32
+ end
33
+
34
+ def txns(start_time, end_time, data)
35
+ payload = common()
36
+ payload[:startTime] = (start_time.to_f * 1000).to_i
37
+ payload[:endTime] = (end_time.to_f * 1000).to_i
38
+ payload[:transactions] = build_txns_obj(data)
39
+ payload
40
+ end
41
+
42
+ def traces(start_time, end_time, data)
43
+ payload = common()
44
+ payload[:startTime] = (start_time.to_f * 1000).to_i
45
+ payload[:endTime] = (end_time.to_f * 1000).to_i
46
+ payload[:traces] = build_traces_obj(data)
47
+ payload
48
+ end
49
+
50
+ def error_metrics(start_time, end_time, metrics_data, requests_data)
51
+ payload = common()
52
+ payload[:startTime] = (start_time.to_f * 1000).to_i
53
+ payload[:endTime] = (end_time.to_f * 1000).to_i
54
+ payload[:errorMetrics] = build_error_metrics_obj(metrics_data)
55
+ payload[:errorRequests] = build_error_requests_obj(requests_data)
56
+ payload
57
+ end
58
+
59
+ def hostinfo(start_time)
60
+ payload = common()
61
+ payload[:timestamp] = (start_time.to_f * 1000).to_i
62
+ payload[:environment] = build_hostinfo_obj()
63
+ payload
64
+ end
65
+
66
+ def errors(start_time, end_time, error_data)
67
+ payload = common()
68
+ payload[:errors] = build_errors_obj(error_data)
69
+ payload
70
+ end
71
+
72
+ def metrics(start_time, end_time, metric_data)
73
+ payload = common()
74
+ payload[:startTime] = (start_time.to_f * 1000).to_i
75
+ payload[:endTime] = (end_time.to_f * 1000).to_i
76
+ payload[:ruby] = metric_data
77
+ payload
78
+ end
79
+
80
+ private
81
+
82
+ def keyword_field(value)
83
+ Util.truncate(value)
84
+ end
85
+
86
+ def build_request(context)
87
+ return unless context
88
+ return unless context.request
89
+
90
+ request = {
91
+ accept: context.request.headers['Accept'],
92
+ 'accept-encoding': context.request.headers['Accept-Encoding'],
93
+ 'accept-language': context.request.headers['Accept-Language'],
94
+ host: context.request.url.hostname,
95
+ port: (context.request.url.port).to_i,
96
+ method: context.request.method,
97
+ userAgent: context.request.headers['User-Agent'],
98
+ path: context.request.url.pathname
99
+ }
100
+ unless context.response.nil?
101
+ unless context.response.status_code.nil?
102
+ request[:statusCode] = context.response.status_code
103
+ end
104
+ end
105
+ request
106
+ end
107
+
108
+ def build_error_requests_obj(requests_data)
109
+ error_requests = []
110
+ requests_data.each do |v|
111
+ error_request = {}
112
+ error_request[:name] = v['name']
113
+ error_request[:type] = @config.framework_name
114
+ error_request[:kind] = "Ruby"
115
+ error_request[:request] = build_request(v['context'])
116
+ error_requests << error_request
117
+ end
118
+ error_requests
119
+ end
120
+
121
+ def build_errors_obj(data)
122
+ errors = []
123
+ data.each do |v|
124
+ error = {}
125
+ error[:timestamp] = v.timestamp
126
+ unless v.transaction.nil?
127
+ error[:transaction] = v.transaction[:name] if v.transaction.key?(:name)
128
+ end
129
+ error[:request] = build_request(v.context)
130
+ error[:exceptions] = []
131
+ exception = {}
132
+ exception[:class] = v.exception.type
133
+ exception[:message] = v.exception.message
134
+ exception[:stacktrace] = []
135
+ unless v.exception.stacktrace.nil?
136
+ unless v.exception.stacktrace.frames.nil?
137
+ v.exception.stacktrace.frames.each do |f|
138
+ frame = {}
139
+ frame[:f] = f.filename
140
+ frame[:m] = f.function
141
+ frame[:ln] = f.lineno
142
+ if f.library_frame == false
143
+ frame[:inp] = true
144
+ unless f.context_line.nil?
145
+ frame[:code] = []
146
+
147
+ unless f.pre_context.nil?
148
+ psize = f.pre_context.size
149
+ lineno = 0
150
+ if f.lineno - psize >= 0
151
+ lineno = f.lineno - psize
152
+ end
153
+ f.pre_context.each do |c|
154
+ frame[:code].push([lineno.to_s, c])
155
+ lineno += 1
156
+ end
157
+ end
158
+
159
+ frame[:code].push([f.lineno.to_s, f.context_line])
160
+
161
+ unless f.post_context.nil?
162
+ psize = f.post_context.size
163
+ lineno = f.lineno + 1
164
+ f.post_context.each do |c|
165
+ frame[:code].push([lineno.to_s, c])
166
+ lineno += 1
167
+ end
168
+ end
169
+ end
170
+ end
171
+ exception[:stacktrace] << frame
172
+ end
173
+ end
174
+ end
175
+ error[:exceptions] << exception
176
+ errors << error
177
+ end
178
+ errors
179
+ end
180
+
181
+ def build_metric(name, value)
182
+ return unless name
183
+ return unless value
184
+
185
+ {
186
+ name: name,
187
+ type: value.type,
188
+ kind: value.kind,
189
+ durations: [value.count, Util.ms(value.min), Util.ms(value.max), Util.ms(value.total)]
190
+ }
191
+ end
192
+
193
+ def build_txns_obj(data)
194
+ txns = []
195
+ data.each do |t, v|
196
+ txn = build_metric(t, v)
197
+ txn[:traces] = []
198
+ v.spans.each do |l, u|
199
+ txn[:traces] << build_metric(l, u)
200
+ end
201
+
202
+ txns << txn
203
+ end
204
+ txns
205
+ end
206
+
207
+ def build_traces_obj(data)
208
+ traces = []
209
+ data.each do |txn|
210
+ trace = {}
211
+ trace[:name] = txn.name
212
+ trace[:type] = @config.framework_name
213
+ trace[:kind] = "Ruby"
214
+ trace[:start] = txn.timestamp
215
+ trace[:duration] = Util.ms(txn.duration)
216
+ trace[:request] = build_request(txn.context)
217
+ trace[:entries] = []
218
+ trace[:funcs] = []
219
+ i = 0
220
+
221
+ unless txn.spans.nil?
222
+ unless txn.spans.empty?
223
+ txn.spans.each do |span|
224
+ entry = {}
225
+ entry[:lv] = 1
226
+ if span.timestamp >= txn.timestamp
227
+ entry[:so] = Util.ms(span.timestamp - txn.timestamp)
228
+ else
229
+ entry[:so] = 0
230
+ end
231
+ entry[:du] = Util.ms(span.duration)
232
+ entry[:ly] = {}
233
+ entry[:ly][:name] = span.name
234
+ entry[:ly][:type] = Atatus::Collector::Layer.span_type(span.subtype)
235
+ entry[:ly][:kind] = Atatus::Collector::Layer.span_kind(span.type)
236
+ unless span.context.nil?
237
+ unless span.context.db.nil?
238
+ unless span.context.db.statement.nil?
239
+ entry[:dt] = {}
240
+ entry[:dt][:query] = span.context.db.statement
241
+ end
242
+ end
243
+ end
244
+ trace[:entries] << entry
245
+ func_index = trace[:funcs].index(span.name)
246
+ if func_index.nil?
247
+ trace[:funcs] << span.name
248
+ func_index = i
249
+ i = i + 1
250
+ end
251
+ entry[:i] = func_index
252
+ end
253
+ end
254
+ end
255
+
256
+ traces << trace
257
+ end
258
+ traces
259
+ end
260
+
261
+ def build_error_metrics_obj(metrics_data)
262
+ error_metrics = []
263
+ metrics_data.each do |t, v|
264
+ error_metric = {}
265
+ error_metric[:name] = t
266
+ error_metric[:type] = @config.framework_name
267
+ error_metric[:kind] = "Ruby"
268
+ error_metric[:statusCodes] = v
269
+ error_metrics << error_metric
270
+ end
271
+ error_metrics
272
+ end
273
+
274
+ def build_hostinfo_obj()
275
+ environment = {}
276
+ environment[:gems] = get_gem_list
277
+ environment[:host] = build_hostinfo_env_host()
278
+ environment[:settings] = build_hostinfo_env_settings()
279
+ environment
280
+ end
281
+
282
+ def build_hostinfo_env_host()
283
+ host_info = {
284
+ cpu: [@metadata.hwinfo.cpuinfo],
285
+ ram: @metadata.hwinfo.meminfo,
286
+ hostname: @metadata.hostname,
287
+ uniqueHostname: Socket.gethostname,
288
+ bootId: @metadata.hwinfo.hostid,
289
+ hostId: @metadata.hwinfo.hostid,
290
+ os: @metadata.osinfo.os,
291
+ kernel: @metadata.osinfo.kernel
292
+ }
293
+ unless @metadata.container.nil?
294
+ host_info[:containerId] = @metadata.container[:id] if @metadata.container.key?(:id)
295
+ end
296
+ host_info
297
+ end
298
+
299
+ def build_hostinfo_env_settings()
300
+ {
301
+ agentVersion: VERSION,
302
+ appName: @config.app_name,
303
+ framework: @config.framework_name,
304
+ frameworkVersion: @config.framework_version,
305
+ logLevel: @config.log_level,
306
+ ruby: RUBY_VERSION
307
+ }
308
+ end
309
+
310
+ def get_gem_list()
311
+ Bundler.rubygems.all_specs.map {|spec| [spec.name, spec.version.to_s]}.sort.to_h
312
+ rescue => e
313
+ # logger.warn("Couldn't fetch Gem information: #{e.message}")
314
+ {}
315
+ end
316
+ end
317
+ end
@@ -0,0 +1,72 @@
1
+ require 'json'
2
+ require 'thread'
3
+ require 'net/http'
4
+ require 'atatus/collector/builder'
5
+ require 'json'
6
+
7
+ module Atatus
8
+ class BaseTransport
9
+ # include Logging
10
+
11
+ TXN_ENDPOINT = '/track/apm/txn'.freeze
12
+ TRACE_ENDPOINT = '/track/apm/trace'.freeze
13
+ HOSTINFO_ENDPOINT = "/track/apm/hostinfo".freeze
14
+ ERROR_ENDPOINT = "/track/apm/error".freeze
15
+ ERROR_METRIC_ENDPOINT = "/track/apm/error_metric".freeze
16
+ METRIC_ENDPOINT = "/track/apm/metric".freeze
17
+
18
+ def initialize(config, hostname, port)
19
+ @config = config
20
+ @hostname = hostname
21
+ @port = port
22
+
23
+ @builder = Atatus::Builder.new(config)
24
+
25
+ @headers = {}
26
+ @headers['Content-Type'] = "application/json"
27
+ end
28
+
29
+ def txn(start_time, end_time, data)
30
+ payload = @builder.txns(start_time, end_time, data)
31
+ post(TXN_ENDPOINT, payload)
32
+ end
33
+
34
+ def trace(start_time, end_time, data)
35
+ payload = @builder.traces(start_time, end_time, data)
36
+ post(TRACE_ENDPOINT, payload)
37
+ end
38
+
39
+ def error_metrics(start_time, end_time, metrics_data, requests_data)
40
+ payload = @builder.error_metrics(start_time, end_time, metrics_data, requests_data)
41
+ post(ERROR_METRIC_ENDPOINT, payload)
42
+ end
43
+
44
+ def hostinfo(start_time)
45
+ payload = @builder.hostinfo(start_time)
46
+ post(HOSTINFO_ENDPOINT, payload)
47
+ end
48
+
49
+ def errors(start_time, end_time, error_data)
50
+ payload = @builder.errors(start_time, end_time, error_data)
51
+ post(ERROR_ENDPOINT, payload)
52
+ end
53
+
54
+ def metrics(start_time, end_time, metric_data)
55
+ payload = @builder.metrics(start_time, end_time, metric_data)
56
+ post(METRIC_ENDPOINT, payload)
57
+ end
58
+
59
+ private
60
+
61
+ def post(endpoint, data)
62
+ request = Net::HTTP::Post.new(endpoint, @headers)
63
+ request.body = ::JSON.dump(data)
64
+ http = Net::HTTP.new(@hostname, @port)
65
+ http.use_ssl = true
66
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
67
+ response = http.start { |http| http.request(request) }
68
+ rescue StandardError => e
69
+ puts "Sending to Atatus backend failed: ", e.inspect, response # To fix
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,248 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+ require 'yaml'
5
+ require 'erb'
6
+
7
+ require 'atatus/util/prefixed_logger'
8
+
9
+ require 'atatus/config/options'
10
+ require 'atatus/config/duration'
11
+ require 'atatus/config/bytes'
12
+ require 'atatus/config/regexp_list'
13
+
14
+ module Atatus
15
+ # rubocop:disable Metrics/ClassLength
16
+ # @api private
17
+ class Config
18
+ extend Options
19
+
20
+ DEPRECATED_OPTIONS = %i[].freeze
21
+
22
+ # rubocop:disable Metrics/LineLength, Layout/ExtraSpacing
23
+ option :app_name, type: :string
24
+ option :license_key, type: :string
25
+ option :trace_threshold, type: :int, default: 2000
26
+ option :config_file, type: :string, default: 'config/atatus.yml'
27
+ option :server_url, type: :string, default: 'http://localhost:8200'
28
+ option :secret_token, type: :string
29
+
30
+ option :active, type: :bool, default: true
31
+ option :api_buffer_size, type: :int, default: 256
32
+ option :api_request_size, type: :bytes, default: '750kb', converter: Bytes.new
33
+ option :api_request_time, type: :float, default: '10s', converter: Duration.new
34
+ option :capture_body, type: :string, default: 'off'
35
+ option :capture_headers, type: :bool, default: true
36
+ option :capture_env, type: :bool, default: true
37
+ option :central_config, type: :bool, default: true
38
+ option :current_user_email_method, type: :string, default: 'email'
39
+ option :current_user_id_method, type: :string, default: 'id'
40
+ option :current_user_username_method, type: :string, default: 'username'
41
+ option :custom_key_filters, type: :list, default: [], converter: RegexpList.new
42
+ option :default_tags, type: :dict, default: {}
43
+ option :default_labels, type: :dict, default: {}
44
+ option :disable_send, type: :bool, default: false
45
+ option :disable_start_message, type: :bool, default: false
46
+ option :disabled_instrumentations, type: :list, default: %w[json]
47
+ option :disabled_spies, type: :list, default: []
48
+ option :environment, type: :string, default: ENV['RAILS_ENV'] || ENV['RACK_ENV']
49
+ option :framework_name, type: :string
50
+ option :framework_version, type: :string
51
+ option :filter_exception_types, type: :list, default: []
52
+ option :global_labels, type: :dict
53
+ option :hostname, type: :string
54
+ option :http_compression, type: :bool, default: true
55
+ option :ignore_url_patterns, type: :list, default: [], converter: RegexpList.new
56
+ option :instrument, type: :bool, default: true
57
+ option :instrumented_rake_tasks, type: :list, default: []
58
+ option :log_level, type: :int, default: Logger::INFO
59
+ option :log_path, type: :string, default: '-'
60
+ option :metrics_interval, type: :int, default: '30s', converter: Duration.new
61
+ option :pool_size, type: :int, default: 1
62
+ option :proxy_address, type: :string
63
+ option :proxy_headers, type: :dict
64
+ option :proxy_password, type: :string
65
+ option :proxy_port, type: :int
66
+ option :proxy_username, type: :string
67
+ option :server_ca_cert, type: :string
68
+ option :service_name, type: :string
69
+ option :service_version, type: :string
70
+ option :source_lines_error_app_frames, type: :int, default: 5
71
+ option :source_lines_error_library_frames, type: :int, default: 0
72
+ option :source_lines_span_app_frames, type: :int, default: 5
73
+ option :source_lines_span_library_frames, type: :int, default: 0
74
+ option :span_frames_min_duration, type: :float, default: '5ms', converter: Duration.new(default_unit: 'ms')
75
+ option :stack_trace_limit, type: :int, default: 999_999
76
+ option :transaction_max_spans, type: :int, default: 500
77
+ option :transaction_sample_rate, type: :float, default: 1.0
78
+ option :verify_server_cert, type: :bool, default: true
79
+ # rubocop:enable Metrics/LineLength, Layout/ExtraSpacing
80
+
81
+ # rubocop:disable Metrics/MethodLength
82
+ def initialize(options = {})
83
+ @options = load_schema
84
+
85
+ custom_logger = options.delete(:logger)
86
+
87
+ assign(options)
88
+
89
+ # Pick out config_file specifically as we need it now to load it,
90
+ # but still need the other env vars to have precedence
91
+ env = load_env
92
+ if (env_config_file = env.delete(:config_file))
93
+ self.config_file = env_config_file
94
+ end
95
+
96
+ assign(load_config_file)
97
+ assign(env)
98
+
99
+ yield self if block_given?
100
+
101
+ @logger = custom_logger || build_logger
102
+
103
+ @__view_paths = []
104
+ @__root_path = Dir.pwd
105
+ end
106
+ # rubocop:enable Metrics/MethodLength
107
+
108
+ attr_accessor :__view_paths, :__root_path
109
+ attr_accessor :logger
110
+
111
+ attr_reader :options
112
+
113
+ def assign(update)
114
+ return unless update
115
+ update.each { |key, value| send(:"#{key}=", value) }
116
+ end
117
+
118
+ # rubocop:disable Metrics/MethodLength
119
+ def available_instrumentations
120
+ %w[
121
+ delayed_job
122
+ elasticsearch
123
+ faraday
124
+ http
125
+ json
126
+ mongo
127
+ net_http
128
+ redis
129
+ sequel
130
+ sidekiq
131
+ sinatra
132
+ tilt
133
+ rake
134
+ ]
135
+ end
136
+ # rubocop:enable Metrics/MethodLength
137
+
138
+ def enabled_instrumentations
139
+ available_instrumentations - disabled_instrumentations
140
+ end
141
+
142
+ def method_missing(name, *args)
143
+ return super unless DEPRECATED_OPTIONS.include?(name)
144
+ warn "The option `#{name}' has been removed."
145
+ end
146
+
147
+ def app=(app)
148
+ case app_type?(app)
149
+ when :sinatra
150
+ set_sinatra(app)
151
+ when :rails
152
+ set_rails(app)
153
+ else
154
+ self.service_name = 'ruby'
155
+ end
156
+ end
157
+
158
+ def use_ssl?
159
+ server_url.start_with?('https')
160
+ end
161
+
162
+ def collect_metrics?
163
+ metrics_interval > 0
164
+ end
165
+
166
+ def span_frames_min_duration?
167
+ span_frames_min_duration != 0
168
+ end
169
+
170
+ def span_frames_min_duration=(value)
171
+ super
172
+ @span_frames_min_duration_us = nil
173
+ end
174
+
175
+ def span_frames_min_duration_us
176
+ @span_frames_min_duration_us ||= span_frames_min_duration * 1_000_000
177
+ end
178
+
179
+ def inspect
180
+ super.split.first + '>'
181
+ end
182
+
183
+ private
184
+
185
+ def load_config_file
186
+ return unless File.exist?(config_file)
187
+
188
+ read = File.read(config_file)
189
+ evaled = ERB.new(read).result
190
+ YAML.safe_load(evaled)
191
+ end
192
+
193
+ def load_env
194
+ @options.values.each_with_object({}) do |option, opts|
195
+ next unless (value = ENV[option.env_key])
196
+ opts[option.key] = value
197
+ end
198
+ end
199
+
200
+ def build_logger
201
+ Logger.new(log_path == '-' ? STDOUT : log_path).tap do |logger|
202
+ logger.level = log_level
203
+ end
204
+ end
205
+
206
+ def app_type?(app)
207
+ if defined?(::Rails::Application) && app.is_a?(::Rails::Application)
208
+ return :rails
209
+ end
210
+
211
+ if app.is_a?(Class) && app.superclass.to_s == 'Sinatra::Base'
212
+ return :sinatra
213
+ end
214
+
215
+ nil
216
+ end
217
+
218
+ def set_sinatra(app)
219
+ self.service_name = format_name(service_name || app.to_s)
220
+ self.framework_name = framework_name || 'Sinatra'
221
+ self.framework_version = framework_version || Sinatra::VERSION
222
+ self.__root_path = Dir.pwd
223
+ end
224
+
225
+ def set_rails(app) # rubocop:disable Metrics/AbcSize
226
+ self.service_name ||= format_name(service_name || rails_app_name(app))
227
+ self.framework_name ||= 'Rails'
228
+ self.framework_version ||= ::Rails::VERSION::STRING
229
+ self.logger ||= ::Rails.logger
230
+
231
+ self.__root_path = ::Rails.root.to_s
232
+ self.__view_paths = app.config.paths['app/views'].existent
233
+ end
234
+
235
+ def rails_app_name(app)
236
+ if ::Rails::VERSION::MAJOR >= 6
237
+ app.class.module_parent_name
238
+ else
239
+ app.class.parent_name
240
+ end
241
+ end
242
+
243
+ def format_name(str)
244
+ str && str.gsub('::', '_')
245
+ end
246
+ end
247
+ # rubocop:enable Metrics/ClassLength
248
+ end