elastic-apm 1.1.0 → 2.0.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.
Potentially problematic release.
This version of elastic-apm might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rspec +1 -0
- data/.rubocop.yml +7 -1
- data/CHANGELOG.md +45 -0
- data/Gemfile +17 -12
- data/bench/app.rb +1 -2
- data/bench/benchmark.rb +1 -1
- data/bench/stackprof.rb +1 -1
- data/docs/api.asciidoc +115 -76
- data/docs/configuration.asciidoc +232 -167
- data/docs/context.asciidoc +7 -3
- data/docs/custom-instrumentation.asciidoc +17 -28
- data/docs/index.asciidoc +13 -7
- data/docs/supported-technologies.asciidoc +65 -0
- data/elastic-apm.gemspec +3 -2
- data/lib/elastic_apm.rb +272 -121
- data/lib/elastic_apm/agent.rb +56 -107
- data/lib/elastic_apm/config.rb +130 -106
- data/lib/elastic_apm/config/duration.rb +25 -0
- data/lib/elastic_apm/config/size.rb +28 -0
- data/lib/elastic_apm/context_builder.rb +1 -0
- data/lib/elastic_apm/deprecations.rb +19 -0
- data/lib/elastic_apm/error.rb +5 -2
- data/lib/elastic_apm/error/exception.rb +1 -1
- data/lib/elastic_apm/error_builder.rb +5 -0
- data/lib/elastic_apm/instrumenter.rb +121 -53
- data/lib/elastic_apm/internal_error.rb +1 -0
- data/lib/elastic_apm/{log.rb → logging.rb} +16 -11
- data/lib/elastic_apm/metadata.rb +20 -0
- data/lib/elastic_apm/metadata/process_info.rb +26 -0
- data/lib/elastic_apm/metadata/service_info.rb +56 -0
- data/lib/elastic_apm/metadata/system_info.rb +30 -0
- data/lib/elastic_apm/middleware.rb +31 -15
- data/lib/elastic_apm/normalizers/action_controller.rb +1 -1
- data/lib/elastic_apm/normalizers/action_mailer.rb +1 -1
- data/lib/elastic_apm/normalizers/action_view.rb +3 -3
- data/lib/elastic_apm/normalizers/active_record.rb +2 -1
- data/lib/elastic_apm/railtie.rb +1 -1
- data/lib/elastic_apm/span.rb +59 -29
- data/lib/elastic_apm/span/context.rb +30 -4
- data/lib/elastic_apm/span_helpers.rb +1 -1
- data/lib/elastic_apm/spies/delayed_job.rb +7 -7
- data/lib/elastic_apm/spies/elasticsearch.rb +4 -4
- data/lib/elastic_apm/spies/http.rb +38 -0
- data/lib/elastic_apm/spies/mongo.rb +22 -11
- data/lib/elastic_apm/spies/net_http.rb +7 -4
- data/lib/elastic_apm/spies/rake.rb +5 -6
- data/lib/elastic_apm/spies/redis.rb +1 -1
- data/lib/elastic_apm/spies/sequel.rb +9 -7
- data/lib/elastic_apm/spies/sidekiq.rb +5 -5
- data/lib/elastic_apm/spies/tilt.rb +2 -2
- data/lib/elastic_apm/sql_summarizer.rb +3 -3
- data/lib/elastic_apm/stacktrace_builder.rb +6 -6
- data/lib/elastic_apm/subscriber.rb +3 -3
- data/lib/elastic_apm/traceparent.rb +62 -0
- data/lib/elastic_apm/transaction.rb +62 -93
- data/lib/elastic_apm/transport/base.rb +98 -0
- data/lib/elastic_apm/transport/connection.rb +175 -0
- data/lib/elastic_apm/transport/filters.rb +45 -0
- data/lib/elastic_apm/transport/filters/request_body_filter.rb +31 -0
- data/lib/elastic_apm/transport/filters/secrets_filter.rb +59 -0
- data/lib/elastic_apm/transport/serializers.rb +58 -0
- data/lib/elastic_apm/transport/serializers/error_serializer.rb +59 -0
- data/lib/elastic_apm/transport/serializers/span_serializer.rb +30 -0
- data/lib/elastic_apm/transport/serializers/transaction_serializer.rb +33 -0
- data/lib/elastic_apm/transport/worker.rb +73 -0
- data/lib/elastic_apm/util.rb +11 -8
- data/lib/elastic_apm/version.rb +1 -1
- metadata +40 -21
- data/.travis.yml +0 -5
- data/docs/troubleshooting.asciidoc +0 -28
- data/lib/elastic_apm/filters.rb +0 -46
- data/lib/elastic_apm/filters/request_body_filter.rb +0 -33
- data/lib/elastic_apm/filters/secrets_filter.rb +0 -59
- data/lib/elastic_apm/http.rb +0 -139
- data/lib/elastic_apm/process_info.rb +0 -24
- data/lib/elastic_apm/serializers.rb +0 -28
- data/lib/elastic_apm/serializers/errors.rb +0 -61
- data/lib/elastic_apm/serializers/transactions.rb +0 -51
- data/lib/elastic_apm/service_info.rb +0 -54
- data/lib/elastic_apm/system_info.rb +0 -28
- data/lib/elastic_apm/util/dig.rb +0 -31
- data/lib/elastic_apm/util/inspector.rb +0 -61
- data/lib/elastic_apm/worker.rb +0 -106
data/.travis.yml
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
[[debugging]]
|
2
|
-
=== Debugging APM
|
3
|
-
|
4
|
-
The agent not working for you? There are a few settings that might help:
|
5
|
-
|
6
|
-
[float]
|
7
|
-
[[debugging-debug-transactions]]
|
8
|
-
==== `debug_transactions`
|
9
|
-
|
10
|
-
[options="header"]
|
11
|
-
|============
|
12
|
-
| Environment | `Config` key | Default
|
13
|
-
| `ELASTIC_APM_DEBUG_TRANSACTIONS` | `debug_transactions` | `false`
|
14
|
-
|============
|
15
|
-
|
16
|
-
When on, Elastic APM will log a summary of each transaction when submitted.
|
17
|
-
|
18
|
-
[float]
|
19
|
-
[[debugging-debug-http]]
|
20
|
-
==== `debug_http`
|
21
|
-
|
22
|
-
[options="header"]
|
23
|
-
|============
|
24
|
-
| Environment | `Config` key | Default
|
25
|
-
| `ELASTIC_APM_DEBUG_HTTP` | `debug_http` | `false`
|
26
|
-
|============
|
27
|
-
|
28
|
-
When on, Elastic APM will log debug information from all the requests it makes to APM Server.
|
data/lib/elastic_apm/filters.rb
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'elastic_apm/filters/request_body_filter'
|
4
|
-
require 'elastic_apm/filters/secrets_filter'
|
5
|
-
|
6
|
-
module ElasticAPM
|
7
|
-
# @api private
|
8
|
-
module Filters
|
9
|
-
def self.new(config)
|
10
|
-
Container.new(config)
|
11
|
-
end
|
12
|
-
|
13
|
-
# @api private
|
14
|
-
class Container
|
15
|
-
def initialize(config)
|
16
|
-
@config = config
|
17
|
-
@filters = {
|
18
|
-
request_body: RequestBodyFilter.new(config),
|
19
|
-
secrets: SecretsFilter.new(config)
|
20
|
-
}
|
21
|
-
end
|
22
|
-
|
23
|
-
attr_reader :config
|
24
|
-
|
25
|
-
def add(key, filter)
|
26
|
-
@filters[key] = filter
|
27
|
-
end
|
28
|
-
|
29
|
-
def remove(key)
|
30
|
-
@filters.delete(key)
|
31
|
-
end
|
32
|
-
|
33
|
-
def apply(payload)
|
34
|
-
@filters.reduce(payload) do |result, (_key, filter)|
|
35
|
-
result = filter.call(result)
|
36
|
-
break if result.nil?
|
37
|
-
result
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def length
|
42
|
-
@filters.length
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
@@ -1,33 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ElasticAPM
|
4
|
-
module Filters
|
5
|
-
# @api private
|
6
|
-
class RequestBodyFilter
|
7
|
-
FILTERED = '[FILTERED]'.freeze
|
8
|
-
|
9
|
-
def initialize(config)
|
10
|
-
@config = config
|
11
|
-
end
|
12
|
-
|
13
|
-
def call(payload)
|
14
|
-
strip_body_from payload[:transactions]
|
15
|
-
strip_body_from payload[:errors]
|
16
|
-
|
17
|
-
payload
|
18
|
-
end
|
19
|
-
|
20
|
-
private
|
21
|
-
|
22
|
-
def strip_body_from(arr)
|
23
|
-
return unless arr
|
24
|
-
|
25
|
-
arr.each do |entity|
|
26
|
-
next unless (request = entity.dig(:context, :request))
|
27
|
-
|
28
|
-
request[:body] = FILTERED
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
@@ -1,59 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ElasticAPM
|
4
|
-
module Filters
|
5
|
-
# @api private
|
6
|
-
class SecretsFilter
|
7
|
-
FILTERED = '[FILTERED]'.freeze
|
8
|
-
|
9
|
-
KEY_FILTERS = [
|
10
|
-
/passw(or)?d/i,
|
11
|
-
/^pw$/,
|
12
|
-
/secret/i,
|
13
|
-
/token/i,
|
14
|
-
/api[-._]?key/i,
|
15
|
-
/session[-._]?id/i
|
16
|
-
].freeze
|
17
|
-
|
18
|
-
VALUE_FILTERS = [
|
19
|
-
/^\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}$/ # (probably) credit card number
|
20
|
-
].freeze
|
21
|
-
|
22
|
-
def initialize(config)
|
23
|
-
@config = config
|
24
|
-
@key_filters = KEY_FILTERS + config.custom_key_filters
|
25
|
-
end
|
26
|
-
|
27
|
-
def call(payload)
|
28
|
-
strip_from payload[:transactions], :context, :request, :headers
|
29
|
-
strip_from payload[:transactions], :context, :response, :headers
|
30
|
-
strip_from payload[:errors], :context, :request, :headers
|
31
|
-
strip_from payload[:errors], :context, :response, :headers
|
32
|
-
|
33
|
-
payload
|
34
|
-
end
|
35
|
-
|
36
|
-
def strip_from(events, *path)
|
37
|
-
return unless events
|
38
|
-
|
39
|
-
events.each do |event|
|
40
|
-
next unless (headers = event.dig(*path))
|
41
|
-
|
42
|
-
headers.each do |k, v|
|
43
|
-
if filter_key?(k) || filter_value?(v)
|
44
|
-
headers[k] = FILTERED
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def filter_key?(key)
|
51
|
-
@key_filters.any? { |regex| key.match regex }
|
52
|
-
end
|
53
|
-
|
54
|
-
def filter_value?(value)
|
55
|
-
VALUE_FILTERS.any? { |regex| value.match regex }
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
data/lib/elastic_apm/http.rb
DELETED
@@ -1,139 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'net/http'
|
4
|
-
require 'openssl'
|
5
|
-
require 'zlib'
|
6
|
-
|
7
|
-
require 'elastic_apm/service_info'
|
8
|
-
require 'elastic_apm/system_info'
|
9
|
-
require 'elastic_apm/process_info'
|
10
|
-
require 'elastic_apm/filters'
|
11
|
-
|
12
|
-
module ElasticAPM
|
13
|
-
# @api private
|
14
|
-
class Http
|
15
|
-
include Log
|
16
|
-
|
17
|
-
USER_AGENT = "elastic-apm/ruby #{VERSION}".freeze
|
18
|
-
ACCEPT = 'application/json'.freeze
|
19
|
-
CONTENT_TYPE = 'application/json'.freeze
|
20
|
-
|
21
|
-
def initialize(config, adapter: HttpAdapter)
|
22
|
-
@config = config
|
23
|
-
@adapter = adapter.new(config)
|
24
|
-
@base_payload = {
|
25
|
-
service: ServiceInfo.build(config),
|
26
|
-
process: ProcessInfo.build(config),
|
27
|
-
system: SystemInfo.build(config)
|
28
|
-
}
|
29
|
-
@filters = Filters.new(config)
|
30
|
-
end
|
31
|
-
|
32
|
-
attr_reader :filters
|
33
|
-
|
34
|
-
def post(path, payload = {})
|
35
|
-
payload.merge! @base_payload
|
36
|
-
|
37
|
-
payload = filters.apply(payload)
|
38
|
-
return if payload.nil?
|
39
|
-
|
40
|
-
request = prepare_request path, payload.to_json
|
41
|
-
response = @adapter.perform request
|
42
|
-
|
43
|
-
return nil if response == HttpAdapter::DISABLED
|
44
|
-
return response if response.is_a?(Net::HTTPSuccess)
|
45
|
-
|
46
|
-
error 'POST returned an unsuccessful status code (%d)', response.code
|
47
|
-
error "apm-server's response: %s", response.body
|
48
|
-
|
49
|
-
response
|
50
|
-
end
|
51
|
-
|
52
|
-
private
|
53
|
-
|
54
|
-
def prepare_request(path, data)
|
55
|
-
@adapter.post url_for(path) do |req|
|
56
|
-
req['Accept'] = ACCEPT
|
57
|
-
req['Content-Type'] = CONTENT_TYPE
|
58
|
-
req['User-Agent'] = USER_AGENT
|
59
|
-
|
60
|
-
if (token = @config.secret_token)
|
61
|
-
req['Authorization'] = "Bearer #{token}"
|
62
|
-
end
|
63
|
-
|
64
|
-
prepare_request_body! req, data
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
def prepare_request_body!(req, data)
|
69
|
-
if @config.http_compression &&
|
70
|
-
data.bytesize > @config.compression_minimum_size
|
71
|
-
deflated = Zlib.deflate data, @config.compression_level
|
72
|
-
|
73
|
-
req['Content-Encoding'] = 'deflate'
|
74
|
-
req['Content-Length'] = deflated.bytesize.to_s
|
75
|
-
req.body = deflated
|
76
|
-
else
|
77
|
-
req['Content-Length'] = data.bytesize.to_s
|
78
|
-
req.body = data
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
def url_for(path)
|
83
|
-
"#{@config.server_url}#{path}"
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
# @api private
|
88
|
-
class HttpAdapter
|
89
|
-
DISABLED = 'disabled'.freeze
|
90
|
-
|
91
|
-
def initialize(conf)
|
92
|
-
@config = conf
|
93
|
-
end
|
94
|
-
|
95
|
-
def post(path)
|
96
|
-
req = Net::HTTP::Post.new path
|
97
|
-
yield req if block_given?
|
98
|
-
req
|
99
|
-
end
|
100
|
-
|
101
|
-
def perform(req)
|
102
|
-
return DISABLED if @config.disable_send?
|
103
|
-
|
104
|
-
http.start do |http|
|
105
|
-
http.request req
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
private
|
110
|
-
|
111
|
-
def http
|
112
|
-
return @http if @http
|
113
|
-
|
114
|
-
http = Net::HTTP.new server_uri.host, server_uri.port
|
115
|
-
http.use_ssl = @config.use_ssl?
|
116
|
-
http.verify_mode = verify_mode
|
117
|
-
http.read_timeout = @config.http_read_timeout
|
118
|
-
http.open_timeout = @config.http_open_timeout
|
119
|
-
|
120
|
-
if @config.debug_http
|
121
|
-
http.set_debug_output(@config.logger)
|
122
|
-
end
|
123
|
-
|
124
|
-
@http = http
|
125
|
-
end
|
126
|
-
|
127
|
-
def server_uri
|
128
|
-
@server_uri ||= URI(@config.server_url)
|
129
|
-
end
|
130
|
-
|
131
|
-
def verify_mode
|
132
|
-
if @config.use_ssl? && @config.verify_server_cert?
|
133
|
-
OpenSSL::SSL::VERIFY_PEER
|
134
|
-
else
|
135
|
-
OpenSSL::SSL::VERIFY_NONE
|
136
|
-
end
|
137
|
-
end
|
138
|
-
end
|
139
|
-
end
|
@@ -1,24 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ElasticAPM
|
4
|
-
# @api private
|
5
|
-
class ProcessInfo
|
6
|
-
def initialize(config)
|
7
|
-
@config = config
|
8
|
-
end
|
9
|
-
|
10
|
-
def build
|
11
|
-
pid = $PID || Process.pid
|
12
|
-
return unless pid
|
13
|
-
{
|
14
|
-
argv: ARGV,
|
15
|
-
pid: pid,
|
16
|
-
title: $PROGRAM_NAME
|
17
|
-
}
|
18
|
-
end
|
19
|
-
|
20
|
-
def self.build(config)
|
21
|
-
new(config).build
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'json'
|
4
|
-
|
5
|
-
module ElasticAPM
|
6
|
-
# @api private
|
7
|
-
module Serializers
|
8
|
-
# @api private
|
9
|
-
class Serializer
|
10
|
-
def initialize(config)
|
11
|
-
@config = config
|
12
|
-
end
|
13
|
-
|
14
|
-
private
|
15
|
-
|
16
|
-
def micros_to_time(micros)
|
17
|
-
Time.at(ms(micros) / 1_000)
|
18
|
-
end
|
19
|
-
|
20
|
-
def ms(micros)
|
21
|
-
micros.to_f / 1_000
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
require 'elastic_apm/serializers/transactions'
|
28
|
-
require 'elastic_apm/serializers/errors'
|
@@ -1,61 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ElasticAPM
|
4
|
-
module Serializers
|
5
|
-
# @api private
|
6
|
-
class Errors < Serializer
|
7
|
-
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
8
|
-
def build(error)
|
9
|
-
base = {
|
10
|
-
id: error.id,
|
11
|
-
culprit: error.culprit,
|
12
|
-
timestamp: micros_to_time(error.timestamp).utc.iso8601(3),
|
13
|
-
context: error.context.to_h
|
14
|
-
}
|
15
|
-
|
16
|
-
if (exception = error.exception)
|
17
|
-
base[:exception] = build_exception exception
|
18
|
-
end
|
19
|
-
|
20
|
-
if (log = error.log)
|
21
|
-
base[:log] = build_log log
|
22
|
-
end
|
23
|
-
|
24
|
-
if (transaction_id = error.transaction_id)
|
25
|
-
base[:transaction] = { id: transaction_id }
|
26
|
-
end
|
27
|
-
|
28
|
-
base
|
29
|
-
end
|
30
|
-
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
31
|
-
|
32
|
-
def build_all(errors)
|
33
|
-
{ errors: Array(errors).map { |e| build(e) } }
|
34
|
-
end
|
35
|
-
|
36
|
-
private
|
37
|
-
|
38
|
-
def build_exception(exception)
|
39
|
-
{
|
40
|
-
message: exception.message,
|
41
|
-
type: exception.type,
|
42
|
-
module: exception.module,
|
43
|
-
code: exception.code,
|
44
|
-
attributes: exception.attributes,
|
45
|
-
stacktrace: exception.stacktrace.to_a,
|
46
|
-
handled: exception.handled
|
47
|
-
}
|
48
|
-
end
|
49
|
-
|
50
|
-
def build_log(log)
|
51
|
-
{
|
52
|
-
message: log.message,
|
53
|
-
level: log.level,
|
54
|
-
logger_name: log.logger_name,
|
55
|
-
param_message: log.param_message,
|
56
|
-
stacktrace: log.stacktrace.to_a
|
57
|
-
}
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
@@ -1,51 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ElasticAPM
|
4
|
-
module Serializers
|
5
|
-
# @api private
|
6
|
-
class Transactions < Serializer
|
7
|
-
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
8
|
-
def build(transaction)
|
9
|
-
base = {
|
10
|
-
id: transaction.id,
|
11
|
-
name: transaction.name,
|
12
|
-
type: transaction.type,
|
13
|
-
result: transaction.result.to_s,
|
14
|
-
duration: ms(transaction.duration),
|
15
|
-
timestamp: micros_to_time(transaction.timestamp).utc.iso8601(3),
|
16
|
-
spans: transaction.spans.map { |s| build_span(s) },
|
17
|
-
sampled: transaction.sampled,
|
18
|
-
context: transaction.context.to_h
|
19
|
-
}
|
20
|
-
|
21
|
-
if transaction.dropped_spans > 0
|
22
|
-
base[:span_count] = { dropped: { total: transaction.dropped_spans } }
|
23
|
-
end
|
24
|
-
|
25
|
-
base
|
26
|
-
end
|
27
|
-
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
28
|
-
|
29
|
-
def build_all(transactions)
|
30
|
-
{ transactions: Array(transactions).map { |t| build(t) } }
|
31
|
-
end
|
32
|
-
|
33
|
-
private
|
34
|
-
|
35
|
-
# rubocop:disable Metrics/AbcSize
|
36
|
-
def build_span(span)
|
37
|
-
{
|
38
|
-
id: span.id,
|
39
|
-
parent: span.parent && span.parent.id,
|
40
|
-
name: span.name,
|
41
|
-
type: span.type,
|
42
|
-
start: ms(span.relative_start),
|
43
|
-
duration: ms(span.duration),
|
44
|
-
context: span.context && { db: span.context.to_h },
|
45
|
-
stacktrace: span.stacktrace.to_a
|
46
|
-
}
|
47
|
-
end
|
48
|
-
# rubocop:enable Metrics/AbcSize
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|