elastic-apm 0.1.0 → 0.2.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/.rubocop.yml +3 -0
- data/README.md +7 -1
- data/docs/api.asciidoc +210 -0
- data/docs/configuration.asciidoc +32 -0
- data/docs/context.asciidoc +27 -0
- data/docs/custom-instrumentation.asciidoc +14 -0
- data/docs/getting-started-rack.asciidoc +26 -0
- data/docs/getting-started-rails.asciidoc +13 -0
- data/docs/index.asciidoc +33 -0
- data/elastic-apm.gemspec +1 -0
- data/lib/elastic_apm.rb +54 -7
- data/lib/elastic_apm/agent.rb +20 -4
- data/lib/elastic_apm/config.rb +16 -3
- data/lib/elastic_apm/context.rb +22 -0
- data/lib/elastic_apm/context/request.rb +13 -0
- data/lib/elastic_apm/context/request/socket.rb +21 -0
- data/lib/elastic_apm/context/request/url.rb +44 -0
- data/lib/elastic_apm/context/response.rb +24 -0
- data/lib/elastic_apm/context/user.rb +26 -0
- data/lib/elastic_apm/context_builder.rb +93 -0
- data/lib/elastic_apm/error.rb +6 -3
- data/lib/elastic_apm/error_builder.rb +26 -10
- data/lib/elastic_apm/http.rb +4 -2
- data/lib/elastic_apm/injectors/action_dispatch.rb +1 -1
- data/lib/elastic_apm/instrumenter.rb +18 -0
- data/lib/elastic_apm/middleware.rb +15 -3
- data/lib/elastic_apm/naively_hashable.rb +21 -0
- data/lib/elastic_apm/process_info.rb +22 -0
- data/lib/elastic_apm/serializers/errors.rb +10 -3
- data/lib/elastic_apm/serializers/transactions.rb +4 -2
- data/lib/elastic_apm/service_info.rb +0 -3
- data/lib/elastic_apm/span/context.rb +2 -4
- data/lib/elastic_apm/stacktrace/frame.rb +2 -18
- data/lib/elastic_apm/transaction.rb +20 -7
- data/lib/elastic_apm/version.rb +1 -1
- metadata +20 -4
- data/lib/elastic_apm/error/context.rb +0 -119
data/lib/elastic_apm/http.rb
CHANGED
@@ -4,6 +4,7 @@ require 'net/http'
|
|
4
4
|
|
5
5
|
require 'elastic_apm/service_info'
|
6
6
|
require 'elastic_apm/system_info'
|
7
|
+
require 'elastic_apm/process_info'
|
7
8
|
|
8
9
|
module ElasticAPM
|
9
10
|
# @api private
|
@@ -19,6 +20,7 @@ module ElasticAPM
|
|
19
20
|
@adapter = adapter.new(config)
|
20
21
|
@base_payload = {
|
21
22
|
service: ServiceInfo.build(config),
|
23
|
+
process: ProcessInfo.build(config),
|
22
24
|
system: SystemInfo.build(config)
|
23
25
|
}
|
24
26
|
end
|
@@ -57,7 +59,7 @@ module ElasticAPM
|
|
57
59
|
end
|
58
60
|
|
59
61
|
def url_for(path)
|
60
|
-
"#{@config.
|
62
|
+
"#{@config.server_url}#{path}"
|
61
63
|
end
|
62
64
|
end
|
63
65
|
|
@@ -97,7 +99,7 @@ module ElasticAPM
|
|
97
99
|
end
|
98
100
|
|
99
101
|
def server_uri
|
100
|
-
@uri ||= URI(@config.
|
102
|
+
@uri ||= URI(@config.server_url)
|
101
103
|
end
|
102
104
|
end
|
103
105
|
end
|
@@ -13,6 +13,10 @@ module ElasticAPM
|
|
13
13
|
|
14
14
|
# @api private
|
15
15
|
class TransactionInfo
|
16
|
+
def initialize
|
17
|
+
self.current = nil
|
18
|
+
end
|
19
|
+
|
16
20
|
def current
|
17
21
|
Thread.current[KEY]
|
18
22
|
end
|
@@ -80,6 +84,14 @@ module ElasticAPM
|
|
80
84
|
transaction.span(*args, &block)
|
81
85
|
end
|
82
86
|
|
87
|
+
def set_tag(key, value)
|
88
|
+
transaction.context.tags[key] = value.to_s
|
89
|
+
end
|
90
|
+
|
91
|
+
def set_custom_context(context)
|
92
|
+
transaction.context.custom.merge!(context)
|
93
|
+
end
|
94
|
+
|
83
95
|
def submit_transaction(transaction)
|
84
96
|
@pending_transactions << transaction
|
85
97
|
|
@@ -108,5 +120,11 @@ module ElasticAPM
|
|
108
120
|
|
109
121
|
true
|
110
122
|
end
|
123
|
+
|
124
|
+
def inspect
|
125
|
+
'<ElasticAPM::Instrumenter ' \
|
126
|
+
"current_transaction=#{current_transaction.inspect}" \
|
127
|
+
'>'
|
128
|
+
end
|
111
129
|
end
|
112
130
|
end
|
@@ -10,13 +10,16 @@ module ElasticAPM
|
|
10
10
|
# rubocop:disable Metrics/MethodLength
|
11
11
|
def call(env)
|
12
12
|
begin
|
13
|
-
transaction = ElasticAPM.transaction 'Rack',
|
13
|
+
transaction = ElasticAPM.transaction 'Rack', type_for(env),
|
14
|
+
context: ElasticAPM.build_context(env)
|
15
|
+
|
14
16
|
resp = @app.call env
|
15
|
-
|
17
|
+
|
18
|
+
transaction.submit(resp[0], headers: resp[1]) if transaction
|
16
19
|
rescue InternalError
|
17
20
|
raise # Don't report ElasticAPM errors
|
18
21
|
rescue ::Exception => e
|
19
|
-
ElasticAPM.report(e,
|
22
|
+
ElasticAPM.report(e, handled: false)
|
20
23
|
transaction.submit(500) if transaction
|
21
24
|
raise
|
22
25
|
ensure
|
@@ -26,5 +29,14 @@ module ElasticAPM
|
|
26
29
|
resp
|
27
30
|
end
|
28
31
|
# rubocop:enable Metrics/MethodLength
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def type_for(env)
|
36
|
+
format(
|
37
|
+
'request.%s'.freeze,
|
38
|
+
env.fetch('REQUEST_METHOD'.freeze, 'unknown'.freeze)
|
39
|
+
)
|
40
|
+
end
|
29
41
|
end
|
30
42
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ElasticAPM
|
4
|
+
# @api private
|
5
|
+
module NaivelyHashable
|
6
|
+
def naively_hashable?
|
7
|
+
true
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_h
|
11
|
+
instance_variables.each_with_object({}) do |name, h|
|
12
|
+
key = name.to_s.delete('@').to_sym
|
13
|
+
value = instance_variable_get(name)
|
14
|
+
is_hashable =
|
15
|
+
value.respond_to?(:naively_hashable?) && value.naively_hashable?
|
16
|
+
|
17
|
+
h[key] = is_hashable ? value.to_h : value
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,22 @@
|
|
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
|
+
{
|
12
|
+
argv: ARGV,
|
13
|
+
pid: $PID,
|
14
|
+
title: $PROGRAM_NAME
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.build(config)
|
19
|
+
new(config).build
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -4,19 +4,26 @@ module ElasticAPM
|
|
4
4
|
module Serializers
|
5
5
|
# @api private
|
6
6
|
class Errors < Serializer
|
7
|
+
# rubocop:disable Metrics/MethodLength
|
7
8
|
def build(error)
|
8
9
|
base = {
|
9
|
-
id:
|
10
|
+
id: error.id,
|
10
11
|
culprit: error.culprit,
|
11
|
-
timestamp: micros_to_time(error.timestamp).utc.iso8601
|
12
|
+
timestamp: micros_to_time(error.timestamp).utc.iso8601,
|
13
|
+
context: error.context.to_h
|
12
14
|
}
|
13
15
|
|
14
16
|
if (exception = error.exception)
|
15
17
|
base[:exception] = build_exception exception
|
16
18
|
end
|
17
19
|
|
20
|
+
if (transaction_id = error.transaction_id)
|
21
|
+
base[:transaction] = { id: transaction_id }
|
22
|
+
end
|
23
|
+
|
18
24
|
base
|
19
25
|
end
|
26
|
+
# rubocop:enable Metrics/MethodLength
|
20
27
|
|
21
28
|
def build_all(errors)
|
22
29
|
{ errors: Array(errors).map(&method(:build)) }
|
@@ -32,7 +39,7 @@ module ElasticAPM
|
|
32
39
|
code: exception.code,
|
33
40
|
attributes: exception.attributes,
|
34
41
|
stacktrace: exception.stacktrace.to_a,
|
35
|
-
|
42
|
+
handled: exception.handled
|
36
43
|
}
|
37
44
|
end
|
38
45
|
end
|
@@ -7,7 +7,7 @@ module ElasticAPM
|
|
7
7
|
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
8
8
|
def build(transaction)
|
9
9
|
{
|
10
|
-
id:
|
10
|
+
id: transaction.id,
|
11
11
|
name: transaction.name,
|
12
12
|
type: transaction.type,
|
13
13
|
result: transaction.result.to_s,
|
@@ -23,7 +23,9 @@ module ElasticAPM
|
|
23
23
|
duration: ms(span.duration),
|
24
24
|
context: span.context && { db: span.context.to_h }
|
25
25
|
}
|
26
|
-
end
|
26
|
+
end,
|
27
|
+
sampled: transaction.sampled,
|
28
|
+
context: transaction.context.to_h
|
27
29
|
}
|
28
30
|
end
|
29
31
|
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
@@ -4,6 +4,8 @@ module ElasticAPM
|
|
4
4
|
class Span
|
5
5
|
# @api private
|
6
6
|
class Context
|
7
|
+
include NaivelyHashable
|
8
|
+
|
7
9
|
def initialize(**args)
|
8
10
|
args.each do |key, val|
|
9
11
|
send(:"#{key}=", val)
|
@@ -11,10 +13,6 @@ module ElasticAPM
|
|
11
13
|
end
|
12
14
|
|
13
15
|
attr_accessor :instance, :statement, :type, :user
|
14
|
-
|
15
|
-
def to_h
|
16
|
-
{ instance: instance, statement: statement, type: type, user: user }
|
17
|
-
end
|
18
16
|
end
|
19
17
|
end
|
20
18
|
end
|
@@ -4,6 +4,8 @@ module ElasticAPM
|
|
4
4
|
class Stacktrace
|
5
5
|
# @api private
|
6
6
|
class Frame
|
7
|
+
include NaivelyHashable
|
8
|
+
|
7
9
|
attr_accessor(
|
8
10
|
:abs_path,
|
9
11
|
:filename,
|
@@ -32,24 +34,6 @@ module ElasticAPM
|
|
32
34
|
end
|
33
35
|
# rubocop:enable Metrics/AbcSize
|
34
36
|
|
35
|
-
# rubocop:disable Metrics/MethodLength
|
36
|
-
def to_h
|
37
|
-
{
|
38
|
-
abs_path: abs_path,
|
39
|
-
filename: filename,
|
40
|
-
function: function,
|
41
|
-
vars: vars,
|
42
|
-
pre_context: pre_context,
|
43
|
-
context_line: context_line,
|
44
|
-
post_context: post_context,
|
45
|
-
in_app: in_app,
|
46
|
-
lineno: lineno,
|
47
|
-
module: self.module,
|
48
|
-
coln: colno
|
49
|
-
}
|
50
|
-
end
|
51
|
-
# rubocop:enable Metrics/MethodLength
|
52
|
-
|
53
37
|
private
|
54
38
|
|
55
39
|
def read_lines(path)
|
@@ -3,7 +3,9 @@
|
|
3
3
|
module ElasticAPM
|
4
4
|
# @api private
|
5
5
|
class Transaction
|
6
|
-
|
6
|
+
# rubocop:disable Metrics/MethodLength
|
7
|
+
def initialize(instrumenter, name, type = 'custom', context: nil)
|
8
|
+
@id = SecureRandom.uuid
|
7
9
|
@instrumenter = instrumenter
|
8
10
|
@name = name
|
9
11
|
@type = type
|
@@ -16,20 +18,25 @@ module ElasticAPM
|
|
16
18
|
|
17
19
|
@notifications = [] # for AS::Notifications
|
18
20
|
|
21
|
+
@context = context || Context.new
|
22
|
+
|
23
|
+
@sampled = true
|
24
|
+
|
19
25
|
yield self if block_given?
|
20
26
|
end
|
27
|
+
# rubocop:enable Metrics/MethodLength
|
21
28
|
|
22
|
-
attr_accessor :name, :result, :type
|
23
|
-
attr_reader :duration, :root_span, :timestamp, :spans,
|
29
|
+
attr_accessor :id, :name, :result, :type
|
30
|
+
attr_reader :context, :duration, :root_span, :timestamp, :spans,
|
31
|
+
:notifications, :sampled
|
24
32
|
|
25
33
|
def release
|
26
34
|
@instrumenter.current_transaction = nil
|
27
35
|
end
|
28
36
|
|
29
37
|
def done(result = nil)
|
30
|
-
@result = result
|
31
|
-
|
32
38
|
@duration = Util.micros - @timestamp
|
39
|
+
@result = result
|
33
40
|
|
34
41
|
self
|
35
42
|
end
|
@@ -38,8 +45,10 @@ module ElasticAPM
|
|
38
45
|
!!(@result && @duration)
|
39
46
|
end
|
40
47
|
|
41
|
-
def submit(
|
42
|
-
done
|
48
|
+
def submit(status = nil, headers: {})
|
49
|
+
done status
|
50
|
+
|
51
|
+
context.response = Context::Response.new(status, headers: headers)
|
43
52
|
|
44
53
|
release
|
45
54
|
|
@@ -72,6 +81,10 @@ module ElasticAPM
|
|
72
81
|
spans.reverse.lazy.find(&:running?)
|
73
82
|
end
|
74
83
|
|
84
|
+
def inspect
|
85
|
+
"<ElasticAPM::Transaction id:#{id}>"
|
86
|
+
end
|
87
|
+
|
75
88
|
private
|
76
89
|
|
77
90
|
def next_span_id
|
data/lib/elastic_apm/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: elastic-apm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mikkel Malmberg
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-01-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -43,13 +43,26 @@ files:
|
|
43
43
|
- bin/console
|
44
44
|
- bin/setup
|
45
45
|
- bin/with_framework
|
46
|
+
- docs/api.asciidoc
|
47
|
+
- docs/configuration.asciidoc
|
48
|
+
- docs/context.asciidoc
|
49
|
+
- docs/custom-instrumentation.asciidoc
|
50
|
+
- docs/getting-started-rack.asciidoc
|
51
|
+
- docs/getting-started-rails.asciidoc
|
52
|
+
- docs/index.asciidoc
|
46
53
|
- elastic-apm.gemspec
|
47
54
|
- lib/elastic-apm.rb
|
48
55
|
- lib/elastic_apm.rb
|
49
56
|
- lib/elastic_apm/agent.rb
|
50
57
|
- lib/elastic_apm/config.rb
|
58
|
+
- lib/elastic_apm/context.rb
|
59
|
+
- lib/elastic_apm/context/request.rb
|
60
|
+
- lib/elastic_apm/context/request/socket.rb
|
61
|
+
- lib/elastic_apm/context/request/url.rb
|
62
|
+
- lib/elastic_apm/context/response.rb
|
63
|
+
- lib/elastic_apm/context/user.rb
|
64
|
+
- lib/elastic_apm/context_builder.rb
|
51
65
|
- lib/elastic_apm/error.rb
|
52
|
-
- lib/elastic_apm/error/context.rb
|
53
66
|
- lib/elastic_apm/error/exception.rb
|
54
67
|
- lib/elastic_apm/error/log.rb
|
55
68
|
- lib/elastic_apm/error_builder.rb
|
@@ -66,10 +79,12 @@ files:
|
|
66
79
|
- lib/elastic_apm/internal_error.rb
|
67
80
|
- lib/elastic_apm/log.rb
|
68
81
|
- lib/elastic_apm/middleware.rb
|
82
|
+
- lib/elastic_apm/naively_hashable.rb
|
69
83
|
- lib/elastic_apm/normalizers.rb
|
70
84
|
- lib/elastic_apm/normalizers/action_controller.rb
|
71
85
|
- lib/elastic_apm/normalizers/action_view.rb
|
72
86
|
- lib/elastic_apm/normalizers/active_record.rb
|
87
|
+
- lib/elastic_apm/process_info.rb
|
73
88
|
- lib/elastic_apm/railtie.rb
|
74
89
|
- lib/elastic_apm/serializers.rb
|
75
90
|
- lib/elastic_apm/serializers/errors.rb
|
@@ -92,7 +107,8 @@ files:
|
|
92
107
|
homepage: https://github.com/elastic/apm-agent-ruby
|
93
108
|
licenses:
|
94
109
|
- Apache-2.0
|
95
|
-
metadata:
|
110
|
+
metadata:
|
111
|
+
source_code_uri: https://github.com/elastic/apm-agent-ruby
|
96
112
|
post_install_message:
|
97
113
|
rdoc_options: []
|
98
114
|
require_paths:
|
@@ -1,119 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ElasticAPM
|
4
|
-
class Error
|
5
|
-
# @api private
|
6
|
-
class Context
|
7
|
-
# @api private
|
8
|
-
class Request
|
9
|
-
def initialize
|
10
|
-
@socket = {}
|
11
|
-
@headers = {}
|
12
|
-
@cookies = {}
|
13
|
-
@env = {}
|
14
|
-
end
|
15
|
-
|
16
|
-
attr_accessor(
|
17
|
-
:socket,
|
18
|
-
:http_version,
|
19
|
-
:method,
|
20
|
-
:url,
|
21
|
-
:headers,
|
22
|
-
:cookies,
|
23
|
-
:env,
|
24
|
-
:body
|
25
|
-
)
|
26
|
-
|
27
|
-
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
28
|
-
def add_rack_env(rack_env)
|
29
|
-
req = rails_req?(rack_env) ? rack_env : Rack::Request.new(rack_env)
|
30
|
-
|
31
|
-
self.socket = {
|
32
|
-
remote_address: req.ip,
|
33
|
-
encrypted: req.scheme == 'https'
|
34
|
-
}
|
35
|
-
http_version = rack_env['HTTP_VERSION']
|
36
|
-
self.http_version =
|
37
|
-
http_version && http_version.gsub(%r{HTTP/}, '')
|
38
|
-
self.method = req.request_method
|
39
|
-
self.url = {
|
40
|
-
protocol: req.scheme,
|
41
|
-
hostname: req.host,
|
42
|
-
port: req.port,
|
43
|
-
pathname: req.path,
|
44
|
-
search: req.query_string,
|
45
|
-
hash: nil,
|
46
|
-
raw: req.fullpath
|
47
|
-
}
|
48
|
-
|
49
|
-
add_headers(rack_env)
|
50
|
-
add_body(req)
|
51
|
-
|
52
|
-
self
|
53
|
-
end
|
54
|
-
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
55
|
-
|
56
|
-
def self.from_rack_env(rack_env)
|
57
|
-
request = new
|
58
|
-
request.add_rack_env rack_env
|
59
|
-
request
|
60
|
-
end
|
61
|
-
|
62
|
-
private
|
63
|
-
|
64
|
-
def add_body(req)
|
65
|
-
if req.form_data?
|
66
|
-
self.body = req.POST
|
67
|
-
else
|
68
|
-
self.body = req.body.read
|
69
|
-
req.body.rewind
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
def rails_req?(env)
|
74
|
-
defined?(ActionDispatch::Request) &&
|
75
|
-
env.is_a?(ActionDispatch::Request)
|
76
|
-
end
|
77
|
-
|
78
|
-
def add_headers(rack_env)
|
79
|
-
get_headers(rack_env).each do |key, value|
|
80
|
-
next unless key.upcase == key
|
81
|
-
|
82
|
-
if key.start_with?('HTTP_')
|
83
|
-
headers[camel_key(key)] = value
|
84
|
-
else
|
85
|
-
env[key] = value
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
def camel_key(key)
|
91
|
-
key.gsub(/^HTTP_/, '').split('_').map(&:capitalize).join('-')
|
92
|
-
end
|
93
|
-
|
94
|
-
def get_headers(rack_env)
|
95
|
-
# In Rails < 5 ActionDispatch::Request inherits from Hash
|
96
|
-
rack_env.respond_to?(:headers) ? rack_env.headers : rack_env
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
# @api private
|
101
|
-
class Response
|
102
|
-
attr_accessor(
|
103
|
-
:status_code,
|
104
|
-
:headers,
|
105
|
-
:headers_sent,
|
106
|
-
:finished
|
107
|
-
)
|
108
|
-
end
|
109
|
-
|
110
|
-
attr_accessor(
|
111
|
-
:request,
|
112
|
-
:response,
|
113
|
-
:user,
|
114
|
-
:tags,
|
115
|
-
:custom
|
116
|
-
)
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|