elastic-apm 1.1.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
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
|