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.

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