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.

Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rspec +1 -0
  4. data/.rubocop.yml +7 -1
  5. data/CHANGELOG.md +45 -0
  6. data/Gemfile +17 -12
  7. data/bench/app.rb +1 -2
  8. data/bench/benchmark.rb +1 -1
  9. data/bench/stackprof.rb +1 -1
  10. data/docs/api.asciidoc +115 -76
  11. data/docs/configuration.asciidoc +232 -167
  12. data/docs/context.asciidoc +7 -3
  13. data/docs/custom-instrumentation.asciidoc +17 -28
  14. data/docs/index.asciidoc +13 -7
  15. data/docs/supported-technologies.asciidoc +65 -0
  16. data/elastic-apm.gemspec +3 -2
  17. data/lib/elastic_apm.rb +272 -121
  18. data/lib/elastic_apm/agent.rb +56 -107
  19. data/lib/elastic_apm/config.rb +130 -106
  20. data/lib/elastic_apm/config/duration.rb +25 -0
  21. data/lib/elastic_apm/config/size.rb +28 -0
  22. data/lib/elastic_apm/context_builder.rb +1 -0
  23. data/lib/elastic_apm/deprecations.rb +19 -0
  24. data/lib/elastic_apm/error.rb +5 -2
  25. data/lib/elastic_apm/error/exception.rb +1 -1
  26. data/lib/elastic_apm/error_builder.rb +5 -0
  27. data/lib/elastic_apm/instrumenter.rb +121 -53
  28. data/lib/elastic_apm/internal_error.rb +1 -0
  29. data/lib/elastic_apm/{log.rb → logging.rb} +16 -11
  30. data/lib/elastic_apm/metadata.rb +20 -0
  31. data/lib/elastic_apm/metadata/process_info.rb +26 -0
  32. data/lib/elastic_apm/metadata/service_info.rb +56 -0
  33. data/lib/elastic_apm/metadata/system_info.rb +30 -0
  34. data/lib/elastic_apm/middleware.rb +31 -15
  35. data/lib/elastic_apm/normalizers/action_controller.rb +1 -1
  36. data/lib/elastic_apm/normalizers/action_mailer.rb +1 -1
  37. data/lib/elastic_apm/normalizers/action_view.rb +3 -3
  38. data/lib/elastic_apm/normalizers/active_record.rb +2 -1
  39. data/lib/elastic_apm/railtie.rb +1 -1
  40. data/lib/elastic_apm/span.rb +59 -29
  41. data/lib/elastic_apm/span/context.rb +30 -4
  42. data/lib/elastic_apm/span_helpers.rb +1 -1
  43. data/lib/elastic_apm/spies/delayed_job.rb +7 -7
  44. data/lib/elastic_apm/spies/elasticsearch.rb +4 -4
  45. data/lib/elastic_apm/spies/http.rb +38 -0
  46. data/lib/elastic_apm/spies/mongo.rb +22 -11
  47. data/lib/elastic_apm/spies/net_http.rb +7 -4
  48. data/lib/elastic_apm/spies/rake.rb +5 -6
  49. data/lib/elastic_apm/spies/redis.rb +1 -1
  50. data/lib/elastic_apm/spies/sequel.rb +9 -7
  51. data/lib/elastic_apm/spies/sidekiq.rb +5 -5
  52. data/lib/elastic_apm/spies/tilt.rb +2 -2
  53. data/lib/elastic_apm/sql_summarizer.rb +3 -3
  54. data/lib/elastic_apm/stacktrace_builder.rb +6 -6
  55. data/lib/elastic_apm/subscriber.rb +3 -3
  56. data/lib/elastic_apm/traceparent.rb +62 -0
  57. data/lib/elastic_apm/transaction.rb +62 -93
  58. data/lib/elastic_apm/transport/base.rb +98 -0
  59. data/lib/elastic_apm/transport/connection.rb +175 -0
  60. data/lib/elastic_apm/transport/filters.rb +45 -0
  61. data/lib/elastic_apm/transport/filters/request_body_filter.rb +31 -0
  62. data/lib/elastic_apm/transport/filters/secrets_filter.rb +59 -0
  63. data/lib/elastic_apm/transport/serializers.rb +58 -0
  64. data/lib/elastic_apm/transport/serializers/error_serializer.rb +59 -0
  65. data/lib/elastic_apm/transport/serializers/span_serializer.rb +30 -0
  66. data/lib/elastic_apm/transport/serializers/transaction_serializer.rb +33 -0
  67. data/lib/elastic_apm/transport/worker.rb +73 -0
  68. data/lib/elastic_apm/util.rb +11 -8
  69. data/lib/elastic_apm/version.rb +1 -1
  70. metadata +40 -21
  71. data/.travis.yml +0 -5
  72. data/docs/troubleshooting.asciidoc +0 -28
  73. data/lib/elastic_apm/filters.rb +0 -46
  74. data/lib/elastic_apm/filters/request_body_filter.rb +0 -33
  75. data/lib/elastic_apm/filters/secrets_filter.rb +0 -59
  76. data/lib/elastic_apm/http.rb +0 -139
  77. data/lib/elastic_apm/process_info.rb +0 -24
  78. data/lib/elastic_apm/serializers.rb +0 -28
  79. data/lib/elastic_apm/serializers/errors.rb +0 -61
  80. data/lib/elastic_apm/serializers/transactions.rb +0 -51
  81. data/lib/elastic_apm/service_info.rb +0 -54
  82. data/lib/elastic_apm/system_info.rb +0 -28
  83. data/lib/elastic_apm/util/dig.rb +0 -31
  84. data/lib/elastic_apm/util/inspector.rb +0 -61
  85. data/lib/elastic_apm/worker.rb +0 -106
@@ -5,13 +5,14 @@
5
5
  [float]
6
6
  ==== Adding custom context
7
7
 
8
- You can add your own custom, nested JSON-compatible data to the current transaction using `ElasticAPM.add_custom_context(hash)` eg.:
8
+ You can add your own custom, nested JSON-compatible data to the current
9
+ transaction using `ElasticAPM.set_custom_context(hash)` eg.:
9
10
 
10
11
  [source,ruby]
11
12
  ----
12
13
  class ThingsController < ApplicationController
13
14
  before_action do
14
- ElasticAPM.add_custom_context(company: current_user.company)
15
+ ElasticAPM.set_custom_context(company: current_user.company)
15
16
  end
16
17
 
17
18
  # ...
@@ -21,13 +22,16 @@ end
21
22
  [float]
22
23
  ==== Adding tags
23
24
 
24
- Tags are special in that they are indexed in your Elasticsearch database and therefore searchable.
25
+ Tags are special in that they are indexed in your Elasticsearch database and
26
+ therefore searchable.
25
27
 
26
28
  [source,ruby]
27
29
  ----
28
30
  ElasticAPM.set_tag(:company_name, 'Acme, Inc.')
29
31
  ----
30
32
 
33
+ Note that `.`, `*` and `"` in keys are converted to `_`.
34
+
31
35
  [float]
32
36
  ==== Providing info about the user
33
37
 
@@ -1,19 +1,22 @@
1
1
  [[custom-instrumentation]]
2
2
  === Custom instrumentation
3
3
 
4
- When <<introduction,installed>> and <<configuration,properly configured>> ElasticAPM will automatically wrap your app's request/responses in
5
- transactions and report its errors.
4
+ When <<introduction,installed>> and <<configuration,properly configured>>
5
+ ElasticAPM will automatically wrap your app's request/responses in transactions
6
+ and report its errors.
6
7
  It also wraps each background job if you use Sidekiq or DelayedJob.
7
8
 
8
- But it is possible to create your own transactions as well as provide spans for any
9
- automatic or custom transaction.
9
+ But it is possible to create your own transactions as well as provide spans for
10
+ any automatic or custom transaction.
10
11
 
11
- See <<api-transaction,`ElasticAPM.transaction`>> and <<api-agent-span,`ElasticAPM.span`>>.
12
+ See <<api-agent-start_transaction,`ElasticAPM.start_transaction`>> and
13
+ <<api-agent-start_span,`ElasticAPM.start_span`>>.
12
14
 
13
15
  [float]
14
16
  ==== Helpers
15
17
 
16
- ElasticAPM includes some nifty helpers if you just want to instrument a regular method.
18
+ ElasticAPM includes some nifty helpers if you just want to instrument a regular
19
+ method.
17
20
 
18
21
  [source,ruby]
19
22
  ----
@@ -35,14 +38,14 @@ end
35
38
  [float]
36
39
  ==== Custom span example
37
40
 
38
- If you are already inside a Transaction (most likely) and you want to intrument
41
+ If you are already inside a Transaction (most likely) and you want to instrument
39
42
  some work inside it, add a custom span:
40
43
 
41
44
  [source,ruby]
42
45
  ----
43
46
  class ThingsController < ApplicationController
44
47
  def index
45
- @result_of_work = ElasticAPM.span "Heavy work" do
48
+ @result_of_work = ElasticAPM.with_span "Heavy work" do
46
49
  do_the_heavy_work
47
50
  end
48
51
  end
@@ -52,40 +55,26 @@ end
52
55
  [float]
53
56
  ==== Custom transaction example
54
57
 
55
- If you are **not** inside a Transaction already (eg. outside of your common web application)
56
- start and manage your own transactions like so:
58
+ If you are **not** inside a Transaction already (eg. outside of your common web
59
+ application) start and manage your own transactions like so:
57
60
 
58
61
  [source,ruby]
59
62
  ----
60
63
  class Something
61
64
  def do_work
62
- transaction = ElasticAPM.transaction 'Something#do_work'
65
+ transaction = ElasticAPM.start_transaction 'Something#do_work'
63
66
 
64
67
  begin
65
68
  Sequel[:users] # many third party libs will be automatically instrumented
66
- transaction.submit('success') if transaction
67
69
  rescue Exception => e
68
70
  ElasticAPM.report(e)
69
- transaction.submit('error') if transaction
70
71
  raise
71
72
  ensure
72
- transaction.release
73
+ ElasticAPM.end_transaction('result')
73
74
  end
74
75
  end
75
76
  end
76
77
  ----
77
78
 
78
- **Note:** If the agent isn't started beforehand this will do nothing. See <<api-agent-start,ElasticAPM.start>>.
79
-
80
- [[spies]]
81
- === Spies
82
-
83
- [float]
84
- ==== Automatic integrations with third-party libraries
85
-
86
- ElasticAPM has built-in integrations for some popular libraries.
87
- Use `config.disabled_spies` to disable specific integrations.
88
-
89
- For a list of available spies, see
90
- https://github.com/elastic/apm-agent-ruby/blob/1.x/lib/elastic_apm/config.rb#L174-L188[config.rb].
91
-
79
+ **Note:** If the agent isn't started beforehand this will do nothing.
80
+ See <<api-agent-start,ElasticAPM.start>>.
@@ -2,7 +2,8 @@
2
2
  include::{asciidoc-dir}/../../shared/attributes.asciidoc[]
3
3
 
4
4
  ifdef::env-github[]
5
- NOTE: For the best reading experience, please view this documentation at https://www.elastic.co/guide/en/apm/agent/ruby[elastic.co]
5
+ NOTE: For the best reading experience, please view this documentation at
6
+ https://www.elastic.co/guide/en/apm/agent/ruby[elastic.co]
6
7
  endif::[]
7
8
 
8
9
  = APM Ruby Agent Reference
@@ -10,18 +11,23 @@ endif::[]
10
11
  [[introduction]]
11
12
  == Introduction
12
13
 
13
- The Elastic APM Ruby Agent sends performance metrics and error logs to an Elastic APM Server.
14
+ The Elastic APM Ruby Agent sends performance metrics and error logs to an
15
+ Elastic APM Server.
14
16
 
15
- It has built-in support for <<getting-started-rails,Ruby on Rails>> and other <<getting-started-rack,Rack-compatible>> applications.
17
+ It has built-in support for <<getting-started-rails,Ruby on Rails>> and other
18
+ <<getting-started-rack,Rack-compatible>> applications.
16
19
 
17
- This agent is one of several components you need to get started collecting APM data. See also:
20
+ This agent is one of several components you need to get started collecting APM
21
+ data. See also:
18
22
 
19
23
  * {apm-server-ref}[APM Server]
20
24
 
21
25
  [[framework-support]]
22
- The Elastic APM Ruby Agent officially supports <<getting-started-rails,Ruby on Rails>> versions 4.x on onwards.
26
+ The Elastic APM Ruby Agent officially supports Ruby on Rails versions 4.x on
27
+ onwards, see <<getting-started-rails,Getting started with Ruby on Rails>>.
23
28
 
24
- For Sinatra and other Rack compatible frameworks, see <<getting-started-rack,Getting started with Rack>>.
29
+ For Sinatra and other Rack compatible frameworks, see
30
+ <<getting-started-rack,Getting started with Rack>>.
25
31
 
26
32
  include::./getting-started-rails.asciidoc[]
27
33
  include::./getting-started-rack.asciidoc[]
@@ -31,7 +37,7 @@ include::./configuration.asciidoc[]
31
37
  == Advanced Topics
32
38
  include::./context.asciidoc[]
33
39
  include::./custom-instrumentation.asciidoc[]
34
- include::./troubleshooting.asciidoc[]
35
40
 
41
+ include::./supported-technologies.asciidoc[]
36
42
  include::./api.asciidoc[]
37
43
 
@@ -0,0 +1,65 @@
1
+ [[supported-technologies]]
2
+ == Supported technologies
3
+
4
+ The Elastic APM Ruby Agent has built-in support for many frameworks and
5
+ libraries.
6
+
7
+ Generally we want to support all the most popular libraries, so if your favorite
8
+ is missing feel free so request it in an issue or better yet supply a pull
9
+ request.
10
+
11
+ [float]
12
+ [[supported-technologies-ruby]]
13
+ === Ruby
14
+
15
+ We follow Ruby's own maintenance policy and officially support all currently
16
+ maintained versions per
17
+ https://www.ruby-lang.org/en/downloads/branches/[Ruby Maintenance Branches].
18
+
19
+ [float]
20
+ [[supported-technologies-web]]
21
+ === Web Frameworks and Libraries
22
+
23
+ We have automatic support for Ruby on Rails and all Rack compatible web
24
+ frameworks.
25
+
26
+ We test against all supported minor versions of Rails and Sinatra.
27
+
28
+ [float]
29
+ [[supported-technologies-rails]]
30
+ ==== Ruby on Rails
31
+
32
+ We currently support all versions of Rails since 4.2.
33
+ This follows Rails' own https://rubyonrails.org/security/[Security policy].
34
+
35
+ See <<getting-started-rails>>.
36
+
37
+ [float]
38
+ [[supported-technologies-sinatra]]
39
+ ==== Sinatra
40
+
41
+ We currently support all versions of Sinatra since 1.0.
42
+
43
+ See <<getting-started-rack>>.
44
+
45
+ [float]
46
+ [[supported-technologies-databases]]
47
+ === Databases
48
+
49
+ We automatically instrument database actions using:
50
+
51
+ - ActiveRecord
52
+ - Elasticsearch
53
+ - Mongo
54
+ - Redis
55
+ - Sequel
56
+
57
+ [float]
58
+ [[supported-technologies-http]]
59
+ === External HTTP requests
60
+
61
+ We automatically instrument and add support for distributed tracing to external
62
+ requests using these libraries:
63
+
64
+ - `net/http`
65
+ - Http.rb
@@ -12,13 +12,14 @@ Gem::Specification.new do |spec|
12
12
  spec.homepage = 'https://github.com/elastic/apm-agent-ruby'
13
13
  spec.metadata = { 'source_code_uri' => 'https://github.com/elastic/apm-agent-ruby' }
14
14
  spec.license = 'Apache-2.0'
15
- spec.required_ruby_version = ">= 2.2.0"
15
+ spec.required_ruby_version = ">= 2.3.0"
16
16
 
17
17
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
18
  f.match(%r{^(test|spec|features)/})
19
19
  end
20
20
 
21
- spec.add_dependency('concurrent-ruby', '~> 1.0.0')
21
+ spec.add_dependency('concurrent-ruby', '~> 1.0')
22
+ spec.add_dependency('http', '>= 3.0')
22
23
 
23
24
  spec.require_paths = ['lib']
24
25
  end
@@ -1,16 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'elastic_apm/version'
4
- require 'elastic_apm/log'
5
- require 'elastic_apm/util/dig'
4
+ require 'elastic_apm/internal_error'
5
+ require 'elastic_apm/logging'
6
+ require 'elastic_apm/deprecations'
6
7
 
7
8
  # Core
8
9
  require 'elastic_apm/agent'
9
10
  require 'elastic_apm/config'
10
11
  require 'elastic_apm/context'
11
12
  require 'elastic_apm/instrumenter'
12
- require 'elastic_apm/internal_error'
13
- require 'elastic_apm/span_helpers'
14
13
  require 'elastic_apm/util'
15
14
 
16
15
  require 'elastic_apm/middleware'
@@ -18,142 +17,294 @@ require 'elastic_apm/middleware'
18
17
  require 'elastic_apm/railtie' if defined?(::Rails::Railtie)
19
18
 
20
19
  # ElasticAPM
21
- module ElasticAPM
22
- ### Life cycle
23
-
24
- # Starts the ElasticAPM Agent
25
- #
26
- # @param config [Config] An instance of Config
27
- # @return [Agent] The resulting [Agent]
28
- def self.start(config = {})
29
- Agent.start config
30
- end
20
+ module ElasticAPM # rubocop:disable Metrics/ModuleLength
21
+ class << self
22
+ extend ElasticAPM::Deprecations
31
23
 
32
- # Stops the ElasticAPM Agent
33
- def self.stop
34
- Agent.stop
35
- end
24
+ ### Life cycle
36
25
 
37
- # @return [Boolean] Whether there's an [Agent] running
38
- def self.running?
39
- Agent.running?
40
- end
26
+ # Starts the ElasticAPM Agent
27
+ #
28
+ # @param config [Config] An instance of Config
29
+ # @return [Agent] The resulting [Agent]
30
+ def start(config = {})
31
+ Agent.start config
32
+ end
41
33
 
42
- # @return [Agent] Currently running [Agent] if any
43
- def self.agent
44
- Agent.instance
45
- end
34
+ # Stops the ElasticAPM Agent
35
+ def stop
36
+ Agent.stop
37
+ end
46
38
 
47
- ### Metrics
39
+ # @return [Boolean] Whether there's an [Agent] running
40
+ def running?
41
+ Agent.running?
42
+ end
48
43
 
49
- # Returns the currently active transaction (if any)
50
- #
51
- # @return [Transaction] if any
52
- def self.current_transaction
53
- agent && agent.current_transaction
54
- end
44
+ # @return [Agent] Currently running [Agent] if any
45
+ def agent
46
+ Agent.instance
47
+ end
55
48
 
56
- # Start a new transaction or return the currently running
57
- #
58
- # @param name [String] A description of the transaction, eg
59
- # `ExamplesController#index`
60
- # @param type [String] The kind of the transaction, eg `app.request.get` or
61
- # `db.mysql2.query`
62
- # @param context [Context] An optional [Context]
63
- # @yield [Transaction] Optional block encapsulating transaction
64
- # @return [Transaction] Unless block given
65
- def self.transaction(name = nil, type = nil, context: nil, &block)
66
- return (block_given? ? yield : nil) unless agent
67
-
68
- agent.transaction(name, type, context: context, &block)
69
- end
49
+ ### Metrics
50
+
51
+ # Returns the currently active transaction (if any)
52
+ #
53
+ # @return [Transaction] or `nil`
54
+ def current_transaction
55
+ agent&.current_transaction
56
+ end
57
+
58
+ # Returns the currently active span (if any)
59
+ #
60
+ # @return [Span] or `nil`
61
+ def current_span
62
+ agent&.current_span
63
+ end
64
+
65
+ # Start a new transaction or return the currently running
66
+ #
67
+ # @param name [String] A description of the transaction, eg
68
+ # `ExamplesController#index`
69
+ # @param type [String] The kind of the transaction, eg `app.request.get` or
70
+ # `db.mysql2.query`
71
+ # @param context [Context] An optional [Context]
72
+ # @yield [Transaction] Optional block encapsulating transaction
73
+ # @return [Transaction] Unless block given
74
+ # @deprecated See `with_transaction` or `start_transaction`
75
+ def transaction(name = nil, type = nil, context: nil, &block)
76
+ return (block_given? ? yield : nil) unless agent
77
+
78
+ if block_given?
79
+ with_transaction(name, type, context: context, &block)
80
+ else
81
+ start_transaction(name, type, context: context)
82
+ end
83
+ end
84
+
85
+ deprecate :transaction, :with_transaction
86
+
87
+ # Start a new transaction
88
+ #
89
+ # @param name [String] A description of the transaction, eg
90
+ # `ExamplesController#index`
91
+ # @param type [String] The kind of the transaction, eg `app.request.get` or
92
+ # `db.mysql2.query`
93
+ # @param context [Context] An optional [Context]
94
+ # @return [Transaction]
95
+ def start_transaction(
96
+ name = nil,
97
+ type = nil,
98
+ context: nil,
99
+ traceparent: nil
100
+ )
101
+ agent&.start_transaction(
102
+ name,
103
+ type,
104
+ context: context,
105
+ traceparent: traceparent
106
+ )
107
+ end
108
+
109
+ # Ends the current transaction with `result`
110
+ #
111
+ # @param result [String] The result of the transaction
112
+ # @return [Transaction]
113
+ def end_transaction(result = nil)
114
+ agent&.end_transaction(result)
115
+ end
116
+
117
+ # rubocop:disable Metrics/MethodLength
118
+ # Wrap a block in a Transaction, ending it after the block
119
+ #
120
+ # @param name [String] A description of the transaction, eg
121
+ # `ExamplesController#index`
122
+ # @param type [String] The kind of the transaction, eg `app.request.get` or
123
+ # `db.mysql2.query`
124
+ # @param context [Context] An optional [Context]
125
+ # @yield [Transaction]
126
+ # @return result of block
127
+ def with_transaction(name = nil, type = nil, context: nil, traceparent: nil)
128
+ unless block_given?
129
+ raise ArgumentError,
130
+ 'expected a block. Do you want `start_transaction\' instead?'
131
+ end
132
+
133
+ return yield(nil) unless agent
70
134
 
71
- # Starts a new span under the current Transaction
72
- #
73
- # @param name [String] A description of the span, eq `SELECT FROM "users"`
74
- # @param type [String] The kind of span, eq `db.mysql2.query`
75
- # @param context [Span::Context] Context information about the span
76
- # @yield [Span] Optional block encapsulating span
77
- # @return [Span] Unless block given
78
- def self.span(name, type = nil, context: nil, include_stacktrace: true,
79
- &block)
80
- return (block_given? ? yield : nil) unless agent
81
-
82
- agent.span(
135
+ begin
136
+ transaction =
137
+ start_transaction(
138
+ name,
139
+ type,
140
+ context: context,
141
+ traceparent: traceparent
142
+ )
143
+ yield transaction
144
+ ensure
145
+ end_transaction
146
+ end
147
+ end
148
+ # rubocop:enable Metrics/MethodLength
149
+
150
+ # rubocop:disable Metrics/MethodLength
151
+ # Start a new span
152
+ #
153
+ # @param name [String] A description of the span, eq `SELECT FROM "users"`
154
+ # @param type [String] The kind of span, eq `db.mysql2.query`
155
+ # @param context [Span::Context] Context information about the span
156
+ # @yield [Span] Optional block encapsulating span
157
+ # @return [Span] Unless block given
158
+ # @deprecated See `with_span` or `start_span`
159
+ def span(name, type = nil, context: nil, include_stacktrace: true, &block)
160
+ return (block_given? ? yield : nil) unless agent
161
+
162
+ if block_given?
163
+ with_span(
164
+ name,
165
+ type,
166
+ context:
167
+ context,
168
+ include_stacktrace: include_stacktrace,
169
+ &block
170
+ )
171
+ else
172
+ start_span(
173
+ name,
174
+ type,
175
+ context: context,
176
+ include_stacktrace: include_stacktrace
177
+ )
178
+ end
179
+ end
180
+ # rubocop:enable Metrics/MethodLength
181
+
182
+ deprecate :span, :with_span
183
+
184
+ # Start a new span
185
+ #
186
+ # @param name [String] A description of the span, eq `SELECT FROM "users"`
187
+ # @param type [String] The kind of span, eq `db.mysql2.query`
188
+ # @param context [Span::Context] Context information about the span
189
+ # @param include_stacktrace [Boolean] Whether or not to capture a stacktrace
190
+ # @return [Span]
191
+ def start_span(name, type = nil, context: nil, include_stacktrace: true)
192
+ agent&.start_span(
193
+ name,
194
+ type,
195
+ context: context,
196
+ backtrace: include_stacktrace ? caller : nil
197
+ )
198
+ end
199
+
200
+ # Ends the current span
201
+ #
202
+ # @return [Span]
203
+ def end_span
204
+ agent&.end_span
205
+ end
206
+
207
+ # rubocop:disable Metrics/MethodLength
208
+ # Wrap a block in a Span, ending it after the block
209
+ #
210
+ # @param name [String] A description of the span, eq `SELECT FROM "users"`
211
+ # @param type [String] The kind of span, eq `db.mysql2.query`
212
+ # @param context [Span::Context] Context information about the span
213
+ # @param include_stacktrace [Boolean] Whether or not to capture a stacktrace
214
+ # @yield [Span]
215
+ # @return Result of block
216
+ def with_span(
83
217
  name,
84
- type,
85
- context: context,
86
- backtrace: include_stacktrace ? caller : nil,
87
- &block
218
+ type = nil,
219
+ context: nil,
220
+ include_stacktrace: true
88
221
  )
89
- end
222
+ unless block_given?
223
+ raise ArgumentError,
224
+ 'expected a block. Do you want `start_span\' instead?'
225
+ end
90
226
 
91
- # Build a [Context] from a Rack `env`. The context may include information
92
- # about the request, response, current user and more
93
- #
94
- # @param rack_env [Rack::Env] A Rack env
95
- # @return [Context] The built context
96
- def self.build_context(rack_env)
97
- agent && agent.build_context(rack_env)
98
- end
227
+ return yield nil unless agent
228
+
229
+ begin
230
+ span =
231
+ start_span(
232
+ name, type, context: context, include_stacktrace: include_stacktrace
233
+ )
234
+ yield span
235
+ ensure
236
+ end_span
237
+ end
238
+ end
239
+ # rubocop:enable Metrics/MethodLength
99
240
 
100
- ### Errors
241
+ # Build a [Context] from a Rack `env`. The context may include information
242
+ # about the request, response, current user and more
243
+ #
244
+ # @param rack_env [Rack::Env] A Rack env
245
+ # @return [Context] The built context
246
+ def build_context(rack_env)
247
+ agent&.build_context(rack_env)
248
+ end
101
249
 
102
- # Report and exception to APM
103
- #
104
- # @param exception [Exception] The exception
105
- # @param handled [Boolean] Whether the exception was rescued
106
- # @return [Error] The generated [Error]
107
- def self.report(exception, handled: true)
108
- agent && agent.report(exception, handled: handled)
109
- end
250
+ ### Errors
110
251
 
111
- # Report a custom string error message to APM
112
- #
113
- # @param message [String] The message
114
- # @return [Error] The generated [Error]
115
- def self.report_message(message, **attrs)
116
- agent && agent.report_message(message, backtrace: caller, **attrs)
117
- end
252
+ # Report and exception to APM
253
+ #
254
+ # @param exception [Exception] The exception
255
+ # @param handled [Boolean] Whether the exception was rescued
256
+ # @return [Error] The generated [Error]
257
+ def report(exception, handled: true)
258
+ agent&.report(exception, handled: handled)
259
+ end
118
260
 
119
- ### Context
261
+ # Report a custom string error message to APM
262
+ #
263
+ # @param message [String] The message
264
+ # @return [Error] The generated [Error]
265
+ def report_message(message, **attrs)
266
+ agent&.report_message(message, backtrace: caller, **attrs)
267
+ end
120
268
 
121
- # Set a _tag_ value for the current transaction
122
- #
123
- # @param key [String,Symbol] A key
124
- # @param value [Object] A value (will be converted to string)
125
- # @return [Object] The given value
126
- def self.set_tag(key, value)
127
- agent && agent.set_tag(key, value)
128
- end
269
+ ### Context
129
270
 
130
- # Provide further context for the current transaction
131
- #
132
- # @param custom [Hash] A hash with custom information. Can be nested.
133
- # @return [Hash] The current custom context
134
- def self.set_custom_context(custom)
135
- agent && agent.set_custom_context(custom)
136
- end
271
+ # Set a _tag_ value for the current transaction
272
+ #
273
+ # @param key [String,Symbol] A key
274
+ # @param value [Object] A value (will be converted to string)
275
+ # @return [Object] The given value
276
+ def set_tag(key, value)
277
+ agent&.set_tag(key, value)
278
+ end
137
279
 
138
- # Provide a user to the current transaction
139
- #
140
- # @param user [Object] An object representing a user
141
- # @return [Object] Given user
142
- def self.set_user(user)
143
- agent && agent.set_user(user)
144
- end
280
+ # Provide further context for the current transaction
281
+ #
282
+ # @param custom [Hash] A hash with custom information. Can be nested.
283
+ # @return [Hash] The current custom context
284
+ def set_custom_context(custom)
285
+ agent&.set_custom_context(custom)
286
+ end
145
287
 
146
- # Provide a filter to transform payloads before sending them off
147
- #
148
- # @param key [Symbol] Unique filter key
149
- # @param callback [Object, Proc] A filter that responds to #call(payload)
150
- # @yield [Hash] A filter. Will be used if provided. Otherwise using `callback`
151
- # @return [Bool] true
152
- def self.add_filter(key, callback = nil, &block)
153
- if callback.nil? && !block_given?
154
- raise ArgumentError, '#add_filter needs either `callback\' or a block'
288
+ # Provide a user to the current transaction
289
+ #
290
+ # @param user [Object] An object representing a user
291
+ # @return [Object] Given user
292
+ def set_user(user)
293
+ agent&.set_user(user)
155
294
  end
156
295
 
157
- agent && agent.add_filter(key, block || callback)
296
+ # Provide a filter to transform payloads before sending them off
297
+ #
298
+ # @param key [Symbol] Unique filter key
299
+ # @param callback [Object, Proc] A filter that responds to #call(payload)
300
+ # @yield [Hash] A filter. Used if provided. Otherwise using `callback`
301
+ # @return [Bool] true
302
+ def add_filter(key, callback = nil, &block)
303
+ if callback.nil? && !block_given?
304
+ raise ArgumentError, '#add_filter needs either `callback\' or a block'
305
+ end
306
+
307
+ agent&.add_filter(key, block || callback)
308
+ end
158
309
  end
159
310
  end