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
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'elastic_apm/metadata/service_info'
|
4
|
+
require 'elastic_apm/metadata/system_info'
|
5
|
+
require 'elastic_apm/metadata/process_info'
|
6
|
+
|
7
|
+
module ElasticAPM
|
8
|
+
# @api private
|
9
|
+
module Metadata
|
10
|
+
def self.build(config)
|
11
|
+
{
|
12
|
+
metadata: {
|
13
|
+
service: Metadata::ServiceInfo.build(config),
|
14
|
+
process: Metadata::ProcessInfo.build(config),
|
15
|
+
system: Metadata::SystemInfo.build(config)
|
16
|
+
}
|
17
|
+
}.to_json
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ElasticAPM
|
4
|
+
module Metadata
|
5
|
+
# @api private
|
6
|
+
class ProcessInfo
|
7
|
+
def initialize(config)
|
8
|
+
@config = config
|
9
|
+
end
|
10
|
+
|
11
|
+
def build
|
12
|
+
pid = $PID || Process.pid
|
13
|
+
return unless pid
|
14
|
+
{
|
15
|
+
argv: ARGV,
|
16
|
+
pid: pid,
|
17
|
+
title: $PROGRAM_NAME
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.build(config)
|
22
|
+
new(config).build
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ElasticAPM
|
4
|
+
module Metadata
|
5
|
+
# @api private
|
6
|
+
class ServiceInfo
|
7
|
+
def initialize(config)
|
8
|
+
@config = config
|
9
|
+
end
|
10
|
+
|
11
|
+
# rubocop:disable Metrics/MethodLength
|
12
|
+
def build
|
13
|
+
base = {
|
14
|
+
name: @config.service_name,
|
15
|
+
environment: @config.environment,
|
16
|
+
agent: {
|
17
|
+
name: 'ruby',
|
18
|
+
version: VERSION
|
19
|
+
},
|
20
|
+
framework: nil,
|
21
|
+
language: {
|
22
|
+
name: 'ruby',
|
23
|
+
version: RUBY_VERSION
|
24
|
+
},
|
25
|
+
runtime: runtime,
|
26
|
+
version: @config.service_version || Util.git_sha
|
27
|
+
}
|
28
|
+
|
29
|
+
if @config.framework_name
|
30
|
+
base[:framework] = {
|
31
|
+
name: @config.framework_name,
|
32
|
+
version: @config.framework_version
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
base
|
37
|
+
end
|
38
|
+
# rubocop:enable Metrics/MethodLength
|
39
|
+
|
40
|
+
def self.build(config)
|
41
|
+
new(config).build
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def runtime
|
47
|
+
case RUBY_ENGINE
|
48
|
+
when 'ruby'
|
49
|
+
{ name: RUBY_ENGINE, version: RUBY_VERSION || RUBY_ENGINE_VERSION }
|
50
|
+
when 'jruby'
|
51
|
+
{ name: RUBY_ENGINE, version: JRUBY_VERSION || RUBY_ENGINE_VERSION }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ElasticAPM
|
4
|
+
module Metadata
|
5
|
+
# @api private
|
6
|
+
class SystemInfo
|
7
|
+
def initialize(config)
|
8
|
+
@config = config
|
9
|
+
end
|
10
|
+
|
11
|
+
def build
|
12
|
+
{
|
13
|
+
hostname: @config.hostname || `hostname`.chomp,
|
14
|
+
architecture: platform.cpu,
|
15
|
+
platform: platform.os
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.build(config)
|
20
|
+
new(config).build
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def platform
|
26
|
+
@platform ||= Gem::Platform.local
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -1,40 +1,47 @@
|
|
1
|
+
#
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
4
|
+
require 'elastic_apm/traceparent'
|
5
|
+
|
3
6
|
module ElasticAPM
|
4
7
|
# @api private
|
5
8
|
class Middleware
|
9
|
+
include Logging
|
10
|
+
|
6
11
|
def initialize(app)
|
7
12
|
@app = app
|
8
13
|
end
|
9
14
|
|
10
|
-
# rubocop:disable Metrics/
|
15
|
+
# rubocop:disable Metrics/MethodLength
|
11
16
|
def call(env)
|
12
17
|
begin
|
13
18
|
if running? && !path_ignored?(env)
|
14
|
-
transaction =
|
19
|
+
transaction = start_transaction(env)
|
15
20
|
end
|
16
21
|
|
17
22
|
resp = @app.call env
|
18
|
-
|
19
|
-
status, headers, body = resp
|
20
|
-
submit_transaction(transaction, status, headers, body) if transaction
|
21
23
|
rescue InternalError
|
22
24
|
raise # Don't report ElasticAPM errors
|
23
25
|
rescue ::Exception => e
|
24
26
|
ElasticAPM.report(e, handled: false)
|
25
|
-
transaction.submit('HTTP 5xx', status: 500) if transaction
|
26
27
|
raise
|
27
28
|
ensure
|
28
|
-
|
29
|
+
if resp && transaction
|
30
|
+
status, headers, _body = resp
|
31
|
+
transaction.add_response(status, headers: headers)
|
32
|
+
end
|
33
|
+
|
34
|
+
ElasticAPM.end_transaction http_result(status)
|
29
35
|
end
|
30
36
|
|
31
37
|
resp
|
32
38
|
end
|
33
|
-
# rubocop:enable Metrics/
|
39
|
+
# rubocop:enable Metrics/MethodLength
|
40
|
+
|
41
|
+
private
|
34
42
|
|
35
|
-
def
|
36
|
-
|
37
|
-
transaction.submit(result, status: status, headers: headers)
|
43
|
+
def http_result(status)
|
44
|
+
status && "HTTP #{status.to_s[0]}xx"
|
38
45
|
end
|
39
46
|
|
40
47
|
def path_ignored?(env)
|
@@ -43,9 +50,18 @@ module ElasticAPM
|
|
43
50
|
end
|
44
51
|
end
|
45
52
|
|
46
|
-
def
|
47
|
-
ElasticAPM.
|
48
|
-
context: ElasticAPM.build_context(env)
|
53
|
+
def start_transaction(env)
|
54
|
+
ElasticAPM.start_transaction 'Rack', 'request',
|
55
|
+
context: ElasticAPM.build_context(env),
|
56
|
+
traceparent: traceparent(env)
|
57
|
+
end
|
58
|
+
|
59
|
+
def traceparent(env)
|
60
|
+
return unless (header = env['HTTP_ELASTIC_APM_TRACEPARENT'])
|
61
|
+
Traceparent.parse(header)
|
62
|
+
rescue Traceparent::InvalidTraceparentHeader
|
63
|
+
warn "Couldn't parse invalid traceparent header: #{header.inspect}"
|
64
|
+
nil
|
49
65
|
end
|
50
66
|
|
51
67
|
def running?
|
@@ -53,7 +69,7 @@ module ElasticAPM
|
|
53
69
|
end
|
54
70
|
|
55
71
|
def config
|
56
|
-
ElasticAPM.agent.config
|
72
|
+
@config ||= ElasticAPM.agent.config
|
57
73
|
end
|
58
74
|
end
|
59
75
|
end
|
@@ -6,7 +6,7 @@ module ElasticAPM
|
|
6
6
|
# @api private
|
7
7
|
class ProcessActionNormalizer < Normalizer
|
8
8
|
register 'process_action.action_controller'
|
9
|
-
TYPE = 'app.controller.action'
|
9
|
+
TYPE = 'app.controller.action'
|
10
10
|
|
11
11
|
def normalize(transaction, _name, payload)
|
12
12
|
transaction.name = endpoint(payload)
|
@@ -41,7 +41,7 @@ module ElasticAPM
|
|
41
41
|
# @api private
|
42
42
|
class RenderTemplateNormalizer < RenderNormalizer
|
43
43
|
register 'render_template.action_view'
|
44
|
-
TYPE = 'template.view'
|
44
|
+
TYPE = 'template.view'
|
45
45
|
|
46
46
|
def normalize(_transaction, _name, payload)
|
47
47
|
normalize_render(payload, TYPE)
|
@@ -51,7 +51,7 @@ module ElasticAPM
|
|
51
51
|
# @api private
|
52
52
|
class RenderPartialNormalizer < RenderNormalizer
|
53
53
|
register 'render_partial.action_view'
|
54
|
-
TYPE = 'template.view.partial'
|
54
|
+
TYPE = 'template.view.partial'
|
55
55
|
|
56
56
|
def normalize(_transaction, _name, payload)
|
57
57
|
normalize_render(payload, TYPE)
|
@@ -61,7 +61,7 @@ module ElasticAPM
|
|
61
61
|
# @api private
|
62
62
|
class RenderCollectionNormalizer < RenderNormalizer
|
63
63
|
register 'render_collection.action_view'
|
64
|
-
TYPE = 'template.view.collection'
|
64
|
+
TYPE = 'template.view.collection'
|
65
65
|
|
66
66
|
def normalize(_transaction, _name, payload)
|
67
67
|
normalize_render(payload, TYPE)
|
@@ -20,7 +20,8 @@ module ElasticAPM
|
|
20
20
|
return :skip if %w[SCHEMA CACHE].include?(payload[:name])
|
21
21
|
|
22
22
|
name = summarize(payload[:sql]) || payload[:name]
|
23
|
-
context =
|
23
|
+
context =
|
24
|
+
Span::Context.new(db: { statement: payload[:sql], type: 'sql' })
|
24
25
|
[name, @type, context]
|
25
26
|
end
|
26
27
|
|
data/lib/elastic_apm/railtie.rb
CHANGED
@@ -26,7 +26,7 @@ module ElasticAPM
|
|
26
26
|
app.middleware.insert 0, Middleware
|
27
27
|
end
|
28
28
|
rescue StandardError => e
|
29
|
-
Rails.logger.error "#{
|
29
|
+
Rails.logger.error "#{Logging::PREFIX}Failed to start: #{e.message}"
|
30
30
|
Rails.logger.debug e.backtrace.join("\n")
|
31
31
|
end
|
32
32
|
end
|
data/lib/elastic_apm/span.rb
CHANGED
@@ -7,61 +7,82 @@ require 'elastic_apm/span/context'
|
|
7
7
|
module ElasticAPM
|
8
8
|
# @api private
|
9
9
|
class Span
|
10
|
-
DEFAULT_TYPE = 'custom'
|
10
|
+
DEFAULT_TYPE = 'custom'
|
11
11
|
|
12
12
|
# rubocop:disable Metrics/ParameterLists
|
13
13
|
def initialize(
|
14
|
-
transaction,
|
15
|
-
id,
|
16
14
|
name,
|
17
15
|
type = nil,
|
16
|
+
transaction: nil,
|
18
17
|
parent: nil,
|
19
|
-
context: nil
|
18
|
+
context: nil,
|
19
|
+
stacktrace_builder: nil
|
20
20
|
)
|
21
|
-
@transaction = transaction
|
22
|
-
@id = id
|
23
21
|
@name = name
|
24
22
|
@type = type || DEFAULT_TYPE
|
25
|
-
@parent = parent
|
26
|
-
@context = context
|
27
23
|
|
28
|
-
@
|
29
|
-
|
24
|
+
@id = SecureRandom.hex(8)
|
25
|
+
|
26
|
+
self.transaction = transaction
|
27
|
+
self.parent = parent
|
28
|
+
|
29
|
+
@context = context
|
30
|
+
@stacktrace_builder = stacktrace_builder
|
30
31
|
end
|
31
32
|
# rubocop:enable Metrics/ParameterLists
|
32
33
|
|
33
|
-
attr_accessor :name, :type, :original_backtrace
|
34
|
-
attr_reader :id, :context, :stacktrace, :duration,
|
34
|
+
attr_accessor :name, :type, :original_backtrace, :parent
|
35
|
+
attr_reader :id, :context, :stacktrace, :duration,
|
36
|
+
:relative_start, :timestamp, :transaction_id, :trace_id
|
37
|
+
|
38
|
+
def transaction=(transaction)
|
39
|
+
@transaction_id = transaction&.id
|
40
|
+
@timestamp = transaction&.timestamp
|
41
|
+
@trace_id = transaction&.trace_id
|
42
|
+
end
|
43
|
+
|
44
|
+
def parent_id
|
45
|
+
@parent&.id || transaction_id
|
46
|
+
end
|
47
|
+
|
48
|
+
# life cycle
|
35
49
|
|
36
50
|
def start
|
37
|
-
|
51
|
+
raise 'Transaction needed to start span' unless transaction_id
|
52
|
+
|
53
|
+
@relative_start = Util.micros - timestamp
|
38
54
|
|
39
55
|
self
|
40
56
|
end
|
41
57
|
|
58
|
+
def stop
|
59
|
+
@duration = Util.micros - timestamp - relative_start
|
60
|
+
end
|
61
|
+
|
42
62
|
def done
|
43
|
-
|
63
|
+
stop
|
44
64
|
|
45
|
-
if
|
46
|
-
|
47
|
-
@transaction.instrumenter.agent.stacktrace_builder.build(
|
48
|
-
original_backtrace, type: :span
|
49
|
-
)
|
65
|
+
if should_build_stacktrace?
|
66
|
+
build_stacktrace
|
50
67
|
end
|
51
68
|
|
52
|
-
self.original_backtrace = nil # release it
|
53
|
-
|
54
69
|
self
|
55
70
|
end
|
56
71
|
|
57
|
-
def
|
72
|
+
def stopped?
|
58
73
|
!!duration
|
59
74
|
end
|
60
75
|
|
76
|
+
def started?
|
77
|
+
!!relative_start
|
78
|
+
end
|
79
|
+
|
61
80
|
def running?
|
62
|
-
|
81
|
+
started? && !stopped?
|
63
82
|
end
|
64
83
|
|
84
|
+
# relations
|
85
|
+
|
65
86
|
def inspect
|
66
87
|
"<ElasticAPM::Span id:#{id}" \
|
67
88
|
" name:#{name.inspect}" \
|
@@ -71,14 +92,23 @@ module ElasticAPM
|
|
71
92
|
|
72
93
|
private
|
73
94
|
|
95
|
+
def should_build_stacktrace?
|
96
|
+
@stacktrace_builder && original_backtrace && long_enough_for_stacktrace?
|
97
|
+
end
|
98
|
+
|
99
|
+
def build_stacktrace
|
100
|
+
@stacktrace = @stacktrace_builder.build(original_backtrace, type: :span)
|
101
|
+
self.original_backtrace = nil # release it
|
102
|
+
end
|
103
|
+
|
74
104
|
def long_enough_for_stacktrace?
|
75
|
-
min_duration =
|
105
|
+
min_duration =
|
106
|
+
@stacktrace_builder.config.span_frames_min_duration_us
|
76
107
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
end
|
108
|
+
return true if min_duration < 0
|
109
|
+
return false if min_duration == 0
|
110
|
+
|
111
|
+
duration >= min_duration
|
82
112
|
end
|
83
113
|
end
|
84
114
|
end
|
@@ -6,13 +6,39 @@ module ElasticAPM
|
|
6
6
|
class Context
|
7
7
|
include NaivelyHashable
|
8
8
|
|
9
|
-
def initialize(
|
10
|
-
|
11
|
-
|
9
|
+
def initialize(db: nil, http: nil)
|
10
|
+
@db = db && Db.new(db)
|
11
|
+
@http = http && Http.new(http)
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_accessor :sync, :db, :http
|
15
|
+
|
16
|
+
# @api private
|
17
|
+
class Db
|
18
|
+
include NaivelyHashable
|
19
|
+
|
20
|
+
def initialize(instance: nil, statement: nil, type: nil, user: nil)
|
21
|
+
@instance = instance
|
22
|
+
@statement = statement
|
23
|
+
@type = type
|
24
|
+
@user = user
|
12
25
|
end
|
26
|
+
|
27
|
+
attr_accessor :instance, :statement, :type, :user
|
13
28
|
end
|
14
29
|
|
15
|
-
|
30
|
+
# @api private
|
31
|
+
class Http
|
32
|
+
include NaivelyHashable
|
33
|
+
|
34
|
+
def initialize(url: nil, status_code: nil, method: nil)
|
35
|
+
@url = url
|
36
|
+
@status_code = status_code
|
37
|
+
@method = method
|
38
|
+
end
|
39
|
+
|
40
|
+
attr_accessor :url, :status_code, :method
|
41
|
+
end
|
16
42
|
end
|
17
43
|
end
|
18
44
|
end
|