elastic-apm 0.1.0 → 0.2.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.

Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +3 -0
  4. data/README.md +7 -1
  5. data/docs/api.asciidoc +210 -0
  6. data/docs/configuration.asciidoc +32 -0
  7. data/docs/context.asciidoc +27 -0
  8. data/docs/custom-instrumentation.asciidoc +14 -0
  9. data/docs/getting-started-rack.asciidoc +26 -0
  10. data/docs/getting-started-rails.asciidoc +13 -0
  11. data/docs/index.asciidoc +33 -0
  12. data/elastic-apm.gemspec +1 -0
  13. data/lib/elastic_apm.rb +54 -7
  14. data/lib/elastic_apm/agent.rb +20 -4
  15. data/lib/elastic_apm/config.rb +16 -3
  16. data/lib/elastic_apm/context.rb +22 -0
  17. data/lib/elastic_apm/context/request.rb +13 -0
  18. data/lib/elastic_apm/context/request/socket.rb +21 -0
  19. data/lib/elastic_apm/context/request/url.rb +44 -0
  20. data/lib/elastic_apm/context/response.rb +24 -0
  21. data/lib/elastic_apm/context/user.rb +26 -0
  22. data/lib/elastic_apm/context_builder.rb +93 -0
  23. data/lib/elastic_apm/error.rb +6 -3
  24. data/lib/elastic_apm/error_builder.rb +26 -10
  25. data/lib/elastic_apm/http.rb +4 -2
  26. data/lib/elastic_apm/injectors/action_dispatch.rb +1 -1
  27. data/lib/elastic_apm/instrumenter.rb +18 -0
  28. data/lib/elastic_apm/middleware.rb +15 -3
  29. data/lib/elastic_apm/naively_hashable.rb +21 -0
  30. data/lib/elastic_apm/process_info.rb +22 -0
  31. data/lib/elastic_apm/serializers/errors.rb +10 -3
  32. data/lib/elastic_apm/serializers/transactions.rb +4 -2
  33. data/lib/elastic_apm/service_info.rb +0 -3
  34. data/lib/elastic_apm/span/context.rb +2 -4
  35. data/lib/elastic_apm/stacktrace/frame.rb +2 -18
  36. data/lib/elastic_apm/transaction.rb +20 -7
  37. data/lib/elastic_apm/version.rb +1 -1
  38. metadata +20 -4
  39. data/lib/elastic_apm/error/context.rb +0 -119
@@ -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.server}#{path}"
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.server)
102
+ @uri ||= URI(@config.server_url)
101
103
  end
102
104
  end
103
105
  end
@@ -10,7 +10,7 @@ module ElasticAPM
10
10
  alias render_exception_without_apm render_exception
11
11
 
12
12
  def render_exception(env, exception)
13
- ElasticAPM.report(exception, rack_env: env)
13
+ ElasticAPM.report(exception)
14
14
  render_exception_without_apm env, exception
15
15
  end
16
16
  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', 'request'
13
+ transaction = ElasticAPM.transaction 'Rack', type_for(env),
14
+ context: ElasticAPM.build_context(env)
15
+
14
16
  resp = @app.call env
15
- transaction.submit(resp[0]) if transaction
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, rack_env: env, handled: false)
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: SecureRandom.uuid,
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
- unhandled: !exception.handled
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: SecureRandom.uuid,
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
@@ -19,13 +19,10 @@ module ElasticAPM
19
19
  version: VERSION
20
20
  },
21
21
  framework: nil,
22
- argv: ARGV,
23
22
  language: {
24
23
  name: 'ruby',
25
24
  version: RUBY_VERSION
26
25
  },
27
- pid: $PID,
28
- process_title: $PROGRAM_NAME,
29
26
  runtime: runtime,
30
27
  version: git_sha
31
28
  }
@@ -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
- def initialize(instrumenter, name, type = 'custom')
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, :notifications
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(result = nil)
42
- done result
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ElasticAPM
4
- VERSION = '0.1.0'.freeze
4
+ VERSION = '0.2.0'.freeze
5
5
  end
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.1.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: 2017-12-27 00:00:00.000000000 Z
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