elastic-apm 2.10.1 → 2.11.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2dbd55033ad5acde5c178afb8af7e3c49aea9e64391901ddd571e6eb4a5db139
4
- data.tar.gz: 0c117a6853b5e54af3f1ad2b42e0ff99e4d061c95da66a89ee35f525b7d11fd0
3
+ metadata.gz: 45fe1b55dccff365970dd59f2a4f58d9cb76141fd3651e7ce9c166b796bfce0a
4
+ data.tar.gz: 66a2df97b05471613e7d1b5eecdb739ccbf94514b15730cb065153c7128503da
5
5
  SHA512:
6
- metadata.gz: 4e5a48afa71841e62e52917e71d6201b68d4b7d36636b93e5b0076999e5a6b13a31f6dac0f39501245122d143497648dafef50fa02a15a30dc767482b4a1181d
7
- data.tar.gz: 671f26c821de9e4090aef5f1af8855729d86f4b3b1ef824d3f5114231e9ecb34ef2e1aa81cb9b84ffcfab7a307418bf95a90df4e034075c5d9bc56798344b31c
6
+ metadata.gz: e542f184535be90fee811ceee0fd404f65b59575b0e90a8dc8023807ec5b535f9d1c084b7654f5c59fed9d5521a2cbf5f2bd70dac8691db009be1dd24b07be5f
7
+ data.tar.gz: 7d87ebdbbfc32727017f6f0fa8451640ac3fef289366bc3a077899d874bb346a3b94dbadafe96596f2b1a8163c42d79b1b41ce940c6e1fd8fb358fc96767ca84
data/.ci/Jenkinsfile CHANGED
@@ -23,6 +23,7 @@ pipeline {
23
23
  DOCKER_SECRET = 'secret/apm-team/ci/docker-registry/prod'
24
24
  GITHUB_CHECK_ITS_NAME = 'Integration Tests'
25
25
  ITS_PIPELINE = 'apm-integration-tests-selector-mbp/master'
26
+ RELEASE_SECRET = 'secret/apm-team/ci/apm-agent-ruby-rubygems-release'
26
27
  }
27
28
  options {
28
29
  timeout(time: 2, unit: 'HOURS')
@@ -178,6 +179,39 @@ pipeline {
178
179
  githubNotify(context: "${env.GITHUB_CHECK_ITS_NAME}", description: "${env.GITHUB_CHECK_ITS_NAME} ...", status: 'PENDING', targetUrl: "${env.JENKINS_URL}search/?q=${env.ITS_PIPELINE.replaceAll('/','+')}")
179
180
  }
180
181
  }
182
+ stage('Release') {
183
+ options { skipDefaultCheckout() }
184
+ environment {
185
+ RUBY_DOCKER_TAG = 'ruby:2.6'
186
+ }
187
+ when {
188
+ beforeAgent true
189
+ anyOf {
190
+ tag pattern: 'v\\d+.*', comparator: 'REGEXP'
191
+ }
192
+ }
193
+ steps {
194
+ withGithubNotify(context: 'Release') {
195
+ deleteDir()
196
+ unstash 'source'
197
+ script {
198
+ dir(BASE_DIR){
199
+ docker.image("${env.RUBY_DOCKER_TAG}").inside('-v /etc/passwd:/etc/passwd -v ${HOME}/.ssh:${HOME}/.ssh') {
200
+ withEnv(["HOME=${env.WORKSPACE}/${env.BASE_DIR ?: ''}"]) {
201
+ rubygemsLogin.withApi(secret: "${env.RELEASE_SECRET}") {
202
+ sshagent(['f6c7695a-671e-4f4f-a331-acdce44ff9ba']) {
203
+ sh '.ci/prepare-git-context.sh'
204
+ sh 'gem install rake yard rspec'
205
+ sh 'rake release'
206
+ }
207
+ }
208
+ }
209
+ }
210
+ }
211
+ }
212
+ }
213
+ }
214
+ }
181
215
  }
182
216
  post {
183
217
  cleanup {
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env bash
2
+ set -exo pipefail
3
+
4
+ # Enable git+ssh. Env variables are created on the fly with the gitCheckout
5
+ git config remote.origin.url "git@github.com:${ORG_NAME}/${REPO_NAME}.git"
6
+
7
+ # Enable to fetch branches when cloning with a detached and shallow clone
8
+ git config remote.origin.fetch '+refs/heads/*:refs/remotes/origin/*'
9
+
10
+ # Force the git user details when pushing using the last commit details
11
+ USER_MAIL=$(git log -1 --pretty=format:'%ae')
12
+ USER_NAME=$(git log -1 --pretty=format:'%an')
13
+ git config --global user.email "${USER_MAIL}"
14
+ git config --global user.name "${USER_NAME}"
15
+
16
+ # Checkout the branch as it's detached based by default.
17
+ # See https://issues.jenkins-ci.org/browse/JENKINS-33171
18
+ git fetch --all
19
+ git checkout "${BRANCH_NAME}"
20
+
21
+ # Ensure the master branch points to the original commit to avoid commit injection
22
+ # when running the release pipeline.
23
+ git reset --hard "${GIT_COMMIT}"
data/CHANGELOG.md CHANGED
@@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## 2.11.0 (2019-09-23)
8
+
9
+ ### Added
10
+
11
+ - Add `Rails` module with `#start` method to run Rails setup explicitly ([#522](https://github.com/elastic/apm-agent-ruby/pull/522))
12
+ - Support for log/trace correlation ([#527](https://github.com/elastic/apm-agent-ruby/pull/527))
13
+
14
+ ### Changed
15
+
16
+ - Split dot-separated `span.type` into `.type`, `.subtype` and `.action` (auto-upgrades dot style) ([#531](https://github.com/elastic/apm-agent-ruby/pull/531))
17
+
7
18
  ## 2.10.1
8
19
 
9
20
  ### Fixed
@@ -11,11 +22,14 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html
11
22
  - Fixed loading options from a config file specified by `ELASTIC_APM_CONFIG_FILE` ([#518](https://github.com/elastic/apm-agent-ruby/pull/518))
12
23
  - Fixed an issue with CentralConfig polling not starting ([#525](https://github.com/elastic/apm-agent-ruby/pull/525))
13
24
 
25
+ ### Added
26
+
27
+ - Support for chained exceptions ([#488](https://github.com/elastic/apm-agent-ruby/pull/488))
28
+
14
29
  ## 2.10.0
15
30
 
16
31
  ### Added
17
32
 
18
- - Support for chained exceptions ([#488](https://github.com/elastic/apm-agent-ruby/pull/488))
19
33
  - Add Ruby specific metrics ([#437](https://github.com/elastic/apm-agent-ruby/pull/437))
20
34
  - Support for APM Agent Configuration via Kibana ([#464](https://github.com/elastic/apm-agent-ruby/pull/464))
21
35
  - Change span name format and add command to context's db.statement for `MongoSpy` ([#488](https://github.com/elastic/apm-agent-ruby/pull/490))
data/docs/api.asciidoc CHANGED
@@ -30,9 +30,10 @@ ElasticAPM.start(server_url: 'http://localhost:8200')
30
30
  * `config`: An optional hash or `ElasticAPM::Config` instance with configuration
31
31
  options. See <<configuration,Configuration>>.
32
32
 
33
- If you are using <<getting-started-rails,Ruby on Rails>> this is done
34
- automatically for you.
35
- If not see <<getting-started-rack,Getting started with Rack>>.
33
+ If you are using <<getting-started-rails,Ruby on Rails>> this is done automatically for you.
34
+ If you choose not to require the `elastic_apm` gem and instead want to start the
35
+ agent and hook into Rails manually, see <<rails-start,hooking into Rails explicitly>>.
36
+ If you're not using Rails, see <<getting-started-rack,Getting started with Rack>>.
36
37
 
37
38
  [float]
38
39
  [[api-agent-stop]]
@@ -140,7 +141,9 @@ end
140
141
  Arguments:
141
142
 
142
143
  * `name`: A name for your span. **Required**.
143
- * `type`: The type of work eg. `db.postgresql.query`.
144
+ * `type`: The type of span eg. `db`.
145
+ * `subtype`: The subtype of span eg. `postgresql`.
146
+ * `action`: The action type of span eg. `connect` or `query`.
144
147
  * `context`: An instance of `Span::Context`.
145
148
  * `include_stacktrace`: Whether or not to collect a Stacktrace.
146
149
  * `&block`: An optional block to wrap with the span.
@@ -163,7 +166,9 @@ Wraps a block in a Span.
163
166
  Arguments:
164
167
 
165
168
  * `name`: A name for your span. **Required**.
166
- * `type`: The type of work eg. `db.postgresql.query`.
169
+ * `type`: The type of span eg. `db`.
170
+ * `subtype`: The subtype of span eg. `postgresql`.
171
+ * `action`: The action type of span eg. `connect` or `query`.
167
172
  * `context`: An instance of `Span::Context`.
168
173
  * `include_stacktrace`: Whether or not to collect a Stacktrace.
169
174
  * `&block`: An optional block to wrap with the span.
@@ -186,6 +191,18 @@ Arguments:
186
191
 
187
192
  Returns the built context.
188
193
 
194
+ [float]
195
+ [[rails-start]]
196
+ === Manually hooking into Rails
197
+
198
+ Start the agent and hook into Rails explicitly. This is useful if you skip requiring
199
+ the gem and using the `Railtie`.
200
+
201
+ [source,ruby]
202
+ ----
203
+ ElasticAPM::Rails.start(server_url: 'http://localhost:8200')
204
+ ----
205
+
189
206
  [float]
190
207
  === Errors
191
208
 
data/docs/index.asciidoc CHANGED
@@ -18,6 +18,8 @@ include::./getting-started-rack.asciidoc[]
18
18
 
19
19
  include::./configuration.asciidoc[]
20
20
 
21
+ include::./log-correlation.asciidoc[]
22
+
21
23
  include::./metrics.asciidoc[]
22
24
 
23
25
  include::./advanced.asciidoc[]
@@ -0,0 +1,96 @@
1
+ ifdef::env-github[]
2
+ NOTE: For the best reading experience,
3
+ please view this documentation at https://www.elastic.co/guide/en/apm/agent/ruby[elastic.co]
4
+ endif::[]
5
+
6
+ [[log-correlation]]
7
+ == Log correlation
8
+
9
+ Trace/log correlation can be set up in three different ways.
10
+
11
+ [float]
12
+ [[rails-tagged-logging]]
13
+ ==== Rails TaggedLogging
14
+
15
+ Rails applications configured with an `ActiveSupport::TaggedLogging` logger can append the correlation IDs to log output.
16
+ For example in your `config/environments/production.rb` file, add the following:
17
+
18
+ [source,ruby]
19
+ ----
20
+ config.log_tags = [ :request_id, proc { ElasticAPM.log_ids } ]
21
+
22
+ # Logs will then include the correlation IDs:
23
+ #
24
+ # [transaction.id=c1ae84c8642891eb trace.id=b899fc7915e801b7558e336e4952bafe] Started GET "/" for 127.0.0.1 at 2019-09-16 11:28:46 +0200
25
+ # [transaction.id=c1ae84c8642891eb trace.id=b899fc7915e801b7558e336e4952bafe] Processing by ApplicationController#index as HTML
26
+ # [transaction.id=c1ae84c8642891eb trace.id=b899fc7915e801b7558e336e4952bafe] Rendering text template
27
+ # [transaction.id=c1ae84c8642891eb trace.id=b899fc7915e801b7558e336e4952bafe] Rendered text template (Duration: 0.1ms | Allocations: 17)
28
+ # [transaction.id=c1ae84c8642891eb trace.id=b899fc7915e801b7558e336e4952bafe] Completed 200 OK in 1ms (Views: 0.4ms | Allocations: 171)
29
+ ----
30
+ **Note:** Because of the order in which Rails computes the tags for logs and executes the request, the span id might not be included.
31
+ Consider using `Lograge` instead, as the timing of its hooks allow the span id to be captured in logs.
32
+
33
+ [float]
34
+ [[lograge]]
35
+ ==== Lograge
36
+
37
+ With `lograge` enabled and set up in your Rails application, modify the `custom_options` block in the Rails environment
38
+ configuration file. The returned `Hash` will be included in the structured Lograge logs.
39
+
40
+ [source,ruby]
41
+ ----
42
+ config.lograge.custom_options = lambda do |event|
43
+ ElasticAPM.log_ids do |transaction_id, span_id, trace_id|
44
+ { :'transaction.id' => transaction_id,
45
+ :'span.id' => span_id,
46
+ :'trace.id' => trace_id }
47
+ end
48
+ end
49
+
50
+ # Logs will then include the correlation IDs:
51
+ #
52
+ # I, [2019-09-16T11:59:05.439602 #8674] INFO -- : method=GET path=/ format=html controller=ApplicationController action=index status=200 duration=0.36 view=0.20 transaction.id=56a9186a9257aa08 span.id=8e84a786ab0abbb2 trace.id=1bbab8ac4c7c9584f53eb882ff0dfdd8
53
+ ----
54
+
55
+ You can also nest the ids in a separate document as in the following example:
56
+
57
+ [source,ruby]
58
+ ----
59
+ config.lograge.custom_options = lambda do |event|
60
+ ElasticAPM.log_ids do |transaction_id, span_id, trace_id|
61
+ { elastic_apm: { :'transaction.id' => transaction_id,
62
+ :'span.id' => span_id,
63
+ :'trace.id' => trace_id } }
64
+ end
65
+ end
66
+
67
+ # Logs will then include the correlation IDs in a separate document:
68
+ #
69
+ # I, [2019-09-16T13:39:35.962603 #9327] INFO -- : method=GET path=/ format=html controller=ApplicationController action=index status=200 duration=0.37 view=0.20 elastic_apm={:transaction_id=>"2fb84f5d0c48a296", :span_id=>"2e5c5a7c85f83be7", :trace_id=>"43e1941c4a6fff343a4e018ff7b92000"}
70
+ ----
71
+
72
+ [float]
73
+ [[manually-formatting-logs]]
74
+ ==== Manually formatting logs
75
+
76
+ You can access the correlation ids directly and add them through the log formatter.
77
+
78
+ [source,ruby]
79
+ ----
80
+ require 'elastic_apm'
81
+ require 'logger'
82
+
83
+ logger = Logger.new(STDOUT)
84
+ logger.progname = 'TestRubyApp'
85
+ logger.formatter = proc do |severity, datetime, progname, msg|
86
+ "[#{datetime}][#{progname}][#{severity}][#{ElasticAPM.log_ids}] #{msg}\n"
87
+ end
88
+
89
+ # Logs will then include the correlation IDs:
90
+ #
91
+ # [2019-09-16 11:54:59 +0200][RailsTestApp][INFO][transaction.id=3b92edcccc0a6d1e trace.id=1275686e35de91f776557637e799651e] Started GET "/" for 127.0.0.1 at 2019-09-16 11:54:59 +0200
92
+ # [2019-09-16 11:54:59 +0200][RailsTestApp][INFO][transaction.id=3b92edcccc0a6d1e trace.id=1275686e35de91f776557637e799651e] Processing by ApplicationController#index as HTML
93
+ # [2019-09-16 11:54:59 +0200][RailsTestApp][INFO][transaction.id=3b92edcccc0a6d1e span.id=3bde4e9c85ab359c trace.id=1275686e35de91f776557637e799651e] Rendering text template
94
+ # [2019-09-16 11:54:59 +0200][RailsTestApp][INFO][transaction.id=3b92edcccc0a6d1e span.id=f3d7e32f176d4c93 trace.id=1275686e35de91f776557637e799651e] Rendered text template (Duration: 0.1ms | Allocations: 17)
95
+ # [2019-09-16 11:54:59 +0200][RailsTestApp][INFO][transaction.id=3b92edcccc0a6d1e span.id=3bde4e9c85ab359c trace.id=1275686e35de91f776557637e799651e] Completed 200 OK in 1ms (Views: 0.3ms | Allocations: 187)
96
+ ----
data/lib/elastic_apm.rb CHANGED
@@ -62,6 +62,26 @@ module ElasticAPM # rubocop:disable Metrics/ModuleLength
62
62
  agent&.current_span
63
63
  end
64
64
 
65
+ # rubocop:disable Metrics/AbcSize
66
+ # Get a formatted string containing transaction, span, and trace ids.
67
+ # If a block is provided, the ids are yielded.
68
+ #
69
+ # @yield [String|nil, String|nil, String|nil] The transaction, span, and trace ids.
70
+ # @return [String] Unless block given
71
+ def log_ids
72
+ trace_id = (current_transaction || current_span)&.trace_id
73
+ if block_given?
74
+ return yield(current_transaction&.id, current_span&.id, trace_id)
75
+ end
76
+
77
+ ids = []
78
+ ids << "transaction.id=#{current_transaction.id}" if current_transaction
79
+ ids << "span.id=#{current_span.id}" if current_span
80
+ ids << "trace.id=#{trace_id}" if trace_id
81
+ ids.join(' ')
82
+ end
83
+ # rubocop:enable Metrics/AbcSize
84
+
65
85
  # Start a new transaction or return the currently running
66
86
  #
67
87
  # @param name [String] A description of the transaction, eg
@@ -207,16 +227,21 @@ module ElasticAPM # rubocop:disable Metrics/ModuleLength
207
227
 
208
228
  deprecate :span, :with_span
209
229
 
230
+ # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists
210
231
  # Start a new span
211
232
  #
212
233
  # @param name [String] A description of the span, eq `SELECT FROM "users"`
213
- # @param type [String] The kind of span, eq `db.mysql2.query`
234
+ # @param type [String] The span type, eq `db`
235
+ # @param subtype [String] The span subtype, eq `postgresql`
236
+ # @param action [String] The span action type, eq `connect` or `query`
214
237
  # @param context [Span::Context] Context information about the span
215
238
  # @param include_stacktrace [Boolean] Whether or not to capture a stacktrace
216
239
  # @return [Span]
217
240
  def start_span(
218
241
  name,
219
242
  type = nil,
243
+ subtype: nil,
244
+ action: nil,
220
245
  context: nil,
221
246
  include_stacktrace: true,
222
247
  trace_context: nil
@@ -224,6 +249,8 @@ module ElasticAPM # rubocop:disable Metrics/ModuleLength
224
249
  agent&.start_span(
225
250
  name,
226
251
  type,
252
+ subtype: subtype,
253
+ action: action,
227
254
  context: context,
228
255
  trace_context: trace_context
229
256
  ).tap do |span|
@@ -233,6 +260,7 @@ module ElasticAPM # rubocop:disable Metrics/ModuleLength
233
260
  span.original_backtrace ||= caller
234
261
  end
235
262
  end
263
+ # rubocop:enable Metrics/MethodLength, Metrics/ParameterLists
236
264
 
237
265
  # Ends the current span
238
266
  #
@@ -241,7 +269,7 @@ module ElasticAPM # rubocop:disable Metrics/ModuleLength
241
269
  agent&.end_span
242
270
  end
243
271
 
244
- # rubocop:disable Metrics/MethodLength
272
+ # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists
245
273
  # Wrap a block in a Span, ending it after the block
246
274
  #
247
275
  # @param name [String] A description of the span, eq `SELECT FROM "users"`
@@ -253,6 +281,8 @@ module ElasticAPM # rubocop:disable Metrics/ModuleLength
253
281
  def with_span(
254
282
  name,
255
283
  type = nil,
284
+ subtype: nil,
285
+ action: nil,
256
286
  context: nil,
257
287
  include_stacktrace: true,
258
288
  trace_context: nil
@@ -269,6 +299,8 @@ module ElasticAPM # rubocop:disable Metrics/ModuleLength
269
299
  start_span(
270
300
  name,
271
301
  type,
302
+ subtype: subtype,
303
+ action: action,
272
304
  context: context,
273
305
  include_stacktrace: include_stacktrace,
274
306
  trace_context: trace_context
@@ -278,7 +310,7 @@ module ElasticAPM # rubocop:disable Metrics/ModuleLength
278
310
  end_span
279
311
  end
280
312
  end
281
- # rubocop:enable Metrics/MethodLength
313
+ # rubocop:enable Metrics/MethodLength, Metrics/ParameterLists
282
314
 
283
315
  # Build a [Context] from a Rack `env`. The context may include information
284
316
  # about the request, response, current user and more
@@ -159,9 +159,12 @@ module ElasticAPM
159
159
  instrumenter.end_transaction(result)
160
160
  end
161
161
 
162
+ # rubocop:disable Metrics/ParameterLists
162
163
  def start_span(
163
164
  name = nil,
164
165
  type = nil,
166
+ subtype: nil,
167
+ action: nil,
165
168
  backtrace: nil,
166
169
  context: nil,
167
170
  trace_context: nil
@@ -169,11 +172,14 @@ module ElasticAPM
169
172
  instrumenter.start_span(
170
173
  name,
171
174
  type,
175
+ subtype: subtype,
176
+ action: action,
172
177
  backtrace: backtrace,
173
178
  context: context,
174
179
  trace_context: trace_context
175
180
  )
176
181
  end
182
+ # rubocop:enable Metrics/ParameterLists
177
183
 
178
184
  def end_span
179
185
  instrumenter.end_span
@@ -234,7 +234,7 @@ module ElasticAPM
234
234
  end
235
235
 
236
236
  def app_type?(app)
237
- if defined?(Rails::Application) && app.is_a?(Rails::Application)
237
+ if defined?(::Rails::Application) && app.is_a?(::Rails::Application)
238
238
  return :rails
239
239
  end
240
240
 
@@ -255,15 +255,15 @@ module ElasticAPM
255
255
  def set_rails(app) # rubocop:disable Metrics/AbcSize
256
256
  self.service_name ||= format_name(service_name || rails_app_name(app))
257
257
  self.framework_name ||= 'Ruby on Rails'
258
- self.framework_version ||= Rails::VERSION::STRING
259
- self.logger ||= Rails.logger
258
+ self.framework_version ||= ::Rails::VERSION::STRING
259
+ self.logger ||= ::Rails.logger
260
260
 
261
- self.__root_path = Rails.root.to_s
261
+ self.__root_path = ::Rails.root.to_s
262
262
  self.__view_paths = app.config.paths['app/views'].existent
263
263
  end
264
264
 
265
265
  def rails_app_name(app)
266
- if Rails::VERSION::MAJOR >= 6
266
+ if ::Rails::VERSION::MAJOR >= 6
267
267
  app.class.module_parent_name
268
268
  else
269
269
  app.class.parent_name
@@ -134,9 +134,12 @@ module ElasticAPM
134
134
 
135
135
  # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
136
136
  # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
137
+ # rubocop:disable Metrics/ParameterLists
137
138
  def start_span(
138
139
  name,
139
140
  type = nil,
141
+ subtype: nil,
142
+ action: nil,
140
143
  backtrace: nil,
141
144
  context: nil,
142
145
  trace_context: nil
@@ -155,6 +158,8 @@ module ElasticAPM
155
158
 
156
159
  span = Span.new(
157
160
  name: name,
161
+ subtype: subtype,
162
+ action: action,
158
163
  transaction_id: transaction.id,
159
164
  trace_context: trace_context || parent.trace_context.child,
160
165
  type: type,
@@ -170,6 +175,7 @@ module ElasticAPM
170
175
 
171
176
  span.start
172
177
  end
178
+ # rubocop:enable Metrics/ParameterLists
173
179
  # rubocop:enable Metrics/AbcSize, Metrics/PerceivedComplexity
174
180
  # rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity
175
181
 
@@ -6,11 +6,14 @@ module ElasticAPM
6
6
  # @api private
7
7
  class ProcessActionNormalizer < Normalizer
8
8
  register 'process_action.action_controller'
9
- TYPE = 'app.controller.action'
9
+
10
+ TYPE = 'app'
11
+ SUBTYPE = 'controller'
12
+ ACTION = 'action'
10
13
 
11
14
  def normalize(transaction, _name, payload)
12
15
  transaction.name = endpoint(payload)
13
- [transaction.name, TYPE, nil]
16
+ [transaction.name, TYPE, SUBTYPE, ACTION, nil]
14
17
  end
15
18
 
16
19
  private
@@ -6,10 +6,13 @@ module ElasticAPM
6
6
  # @api private
7
7
  class ProcessActionNormalizer < Normalizer
8
8
  register 'process.action_mailer'
9
- TYPE = 'app.mailer.action'
9
+
10
+ TYPE = 'app'
11
+ SUBTYPE = 'mailer'
12
+ ACTION = 'action'
10
13
 
11
14
  def normalize(_transaction, _name, payload)
12
- [endpoint(payload), TYPE, nil]
15
+ [endpoint(payload), TYPE, SUBTYPE, ACTION, nil]
13
16
  end
14
17
 
15
18
  private
@@ -7,8 +7,8 @@ module ElasticAPM
7
7
  class RenderNormalizer < Normalizer
8
8
  private
9
9
 
10
- def normalize_render(payload, type)
11
- [path_for(payload[:identifier]), type, nil]
10
+ def normalize_render(payload, type, subtype, action)
11
+ [path_for(payload[:identifier]), type, subtype, action, nil]
12
12
  end
13
13
 
14
14
  def path_for(path)
@@ -41,30 +41,35 @@ 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'
45
+ SUBTYPE = 'view'
45
46
 
46
47
  def normalize(_transaction, _name, payload)
47
- normalize_render(payload, TYPE)
48
+ normalize_render(payload, TYPE, SUBTYPE, nil)
48
49
  end
49
50
  end
50
51
 
51
52
  # @api private
52
53
  class RenderPartialNormalizer < RenderNormalizer
53
54
  register 'render_partial.action_view'
54
- TYPE = 'template.view.partial'
55
+ TYPE = 'template'
56
+ SUBTYPE = 'view'
57
+ ACTION = 'partial'
55
58
 
56
59
  def normalize(_transaction, _name, payload)
57
- normalize_render(payload, TYPE)
60
+ normalize_render(payload, TYPE, SUBTYPE, ACTION)
58
61
  end
59
62
  end
60
63
 
61
64
  # @api private
62
65
  class RenderCollectionNormalizer < RenderNormalizer
63
66
  register 'render_collection.action_view'
64
- TYPE = 'template.view.collection'
67
+ TYPE = 'template'
68
+ SUBTYPE = 'view'
69
+ ACTION = 'collection'
65
70
 
66
71
  def normalize(_transaction, _name, payload)
67
- normalize_render(payload, TYPE)
72
+ normalize_render(payload, TYPE, SUBTYPE, ACTION)
68
73
  end
69
74
  end
70
75
  end
@@ -9,10 +9,13 @@ module ElasticAPM
9
9
  class SqlNormalizer < Normalizer
10
10
  register 'sql.active_record'
11
11
 
12
+ TYPE = 'db'
13
+ ACTION = 'sql'
14
+
12
15
  def initialize(*args)
13
16
  super
14
17
 
15
- @type = format('db.%s.sql', lookup_adapter || 'unknown').freeze
18
+ @subtype = lookup_adapter || 'unknown'
16
19
  @summarizer = SqlSummarizer.new
17
20
  end
18
21
 
@@ -22,7 +25,7 @@ module ElasticAPM
22
25
  name = summarize(payload[:sql]) || payload[:name]
23
26
  context =
24
27
  Span::Context.new(db: { statement: payload[:sql], type: 'sql' })
25
- [name, @type, context]
28
+ [name, TYPE, @subtype, ACTION, context]
26
29
  end
27
30
 
28
31
  private
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'elastic_apm/subscriber'
4
+
5
+ module ElasticAPM
6
+ # Module for explicitly starting the ElasticAPM agent and hooking into Rails.
7
+ # It is recommended to use the Railtie instead.
8
+ module Rails
9
+ extend self
10
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
11
+ # rubocop:disable Metrics/CyclomaticComplexity
12
+ # Start the ElasticAPM agent and hook into Rails.
13
+ # Note that the agent won't be started if the Rails console is being used.
14
+ #
15
+ # @param config [Config, Hash] An instance of Config or a Hash config.
16
+ # @return [true, nil] true if the agent was started, nil otherwise.
17
+ def start(config)
18
+ config = Config.new(config) unless config.is_a?(Config)
19
+ if (reason = should_skip?(config))
20
+ unless config.disable_start_message?
21
+ config.logger.info "Skipping because: #{reason}. " \
22
+ "Start manually with `ElasticAPM.start'"
23
+ end
24
+ return
25
+ end
26
+
27
+ ElasticAPM.start(config).tap do |agent|
28
+ attach_subscriber(agent)
29
+ end
30
+
31
+ if ElasticAPM.running? &&
32
+ !ElasticAPM.agent.config.disabled_spies.include?('action_dispatch')
33
+ require 'elastic_apm/spies/action_dispatch'
34
+ end
35
+ ElasticAPM.running?
36
+ rescue StandardError => e
37
+ config.logger.error format('Failed to start: %s', e.message)
38
+ config.logger.debug "Backtrace:\n" + e.backtrace.join("\n")
39
+ end
40
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
41
+ # rubocop:enable Metrics/CyclomaticComplexity
42
+
43
+ private
44
+
45
+ def should_skip?(_config)
46
+ if ::Rails.const_defined? 'Rails::Console'
47
+ return 'Rails console'
48
+ end
49
+
50
+ nil
51
+ end
52
+
53
+ def attach_subscriber(agent)
54
+ return unless agent
55
+
56
+ agent.instrumenter.subscriber = ElasticAPM::Subscriber.new(agent)
57
+ end
58
+ end
59
+ end
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'elastic_apm/subscriber'
3
+ require 'elastic_apm/rails'
4
4
 
5
5
  module ElasticAPM
6
6
  # @api private
7
- class Railtie < Rails::Railtie
7
+ class Railtie < ::Rails::Railtie
8
8
  config.elastic_apm = ActiveSupport::OrderedOptions.new
9
9
 
10
10
  Config.schema.each do |key, args|
@@ -18,55 +18,13 @@ module ElasticAPM
18
18
 
19
19
  # Prepend Rails.root to log_path if present
20
20
  if c.log_path && !c.log_path.start_with?('/')
21
- c.log_path = Rails.root.join(c.log_path)
21
+ c.log_path = ::Rails.root.join(c.log_path)
22
22
  end
23
23
  end
24
24
 
25
- if start(config)
25
+ if Rails.start(config)
26
26
  app.middleware.insert 0, Middleware
27
27
  end
28
28
  end
29
-
30
- config.after_initialize do
31
- if ElasticAPM.running? &&
32
- !ElasticAPM.agent.config.disabled_spies.include?('action_dispatch')
33
- require 'elastic_apm/spies/action_dispatch'
34
- end
35
- end
36
-
37
- private
38
-
39
- # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
40
- def start(config)
41
- if (reason = should_skip?(config))
42
- unless config.disable_start_message?
43
- config.logger.info "Skipping because: #{reason}. " \
44
- "Start manually with `ElasticAPM.start'"
45
- end
46
- return
47
- end
48
-
49
- ElasticAPM.start(config).tap do |agent|
50
- attach_subscriber(agent)
51
- end
52
- rescue StandardError => e
53
- config.logger.error format('Failed to start: %s', e.message)
54
- config.logger.debug "Backtrace:\n" + e.backtrace.join("\n")
55
- end
56
- # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
57
-
58
- def should_skip?(_config)
59
- if Rails.const_defined? 'Rails::Console'
60
- return 'Rails console'
61
- end
62
-
63
- nil
64
- end
65
-
66
- def attach_subscriber(agent)
67
- return unless agent
68
-
69
- agent.instrumenter.subscriber = ElasticAPM::Subscriber.new(agent)
70
- end
71
29
  end
72
30
  end
@@ -14,17 +14,26 @@ module ElasticAPM
14
14
 
15
15
  DEFAULT_TYPE = 'custom'
16
16
 
17
- # rubocop:disable Metrics/ParameterLists
17
+ # rubocop:disable Metrics/ParameterLists, Metrics/MethodLength
18
18
  def initialize(
19
19
  name:,
20
20
  transaction_id:,
21
21
  trace_context:,
22
22
  type: nil,
23
+ subtype: nil,
24
+ action: nil,
23
25
  context: nil,
24
26
  stacktrace_builder: nil
25
27
  )
26
28
  @name = name
27
- @type = type || DEFAULT_TYPE
29
+
30
+ if subtype.nil? && type&.include?('.')
31
+ @type, @subtype, @action = type.split('.')
32
+ else
33
+ @type = type || DEFAULT_TYPE
34
+ @subtype = subtype
35
+ @action = action
36
+ end
28
37
 
29
38
  @transaction_id = transaction_id
30
39
  @trace_context = trace_context
@@ -32,10 +41,23 @@ module ElasticAPM
32
41
  @context = context || Span::Context.new
33
42
  @stacktrace_builder = stacktrace_builder
34
43
  end
35
- # rubocop:enable Metrics/ParameterLists
36
-
37
- attr_accessor :name, :type, :original_backtrace, :trace_context
38
- attr_reader :context, :stacktrace, :duration, :timestamp, :transaction_id
44
+ # rubocop:enable Metrics/ParameterLists, Metrics/MethodLength
45
+
46
+ attr_accessor(
47
+ :action,
48
+ :name,
49
+ :original_backtrace,
50
+ :subtype,
51
+ :trace_context,
52
+ :type
53
+ )
54
+ attr_reader(
55
+ :context,
56
+ :duration,
57
+ :stacktrace,
58
+ :timestamp,
59
+ :transaction_id
60
+ )
39
61
 
40
62
  # life cycle
41
63
 
@@ -5,6 +5,9 @@ module ElasticAPM
5
5
  module Spies
6
6
  # @api private
7
7
  class FaradaySpy
8
+ TYPE = 'ext'
9
+ SUBTYPE = 'faraday'
10
+
8
11
  def self.without_net_http
9
12
  return yield unless defined?(NetHTTPSpy)
10
13
 
@@ -37,9 +40,13 @@ module ElasticAPM
37
40
  end
38
41
 
39
42
  name = "#{method.upcase} #{host}"
40
- type = "ext.faraday.#{method}"
41
43
 
42
- ElasticAPM.with_span name, type do |span|
44
+ ElasticAPM.with_span(
45
+ name,
46
+ TYPE,
47
+ subtype: SUBTYPE,
48
+ action: method.to_s
49
+ ) do |span|
43
50
  ElasticAPM::Spies::FaradaySpy.without_net_http do
44
51
  trace_context = span&.trace_context || transaction.trace_context
45
52
 
@@ -5,6 +5,9 @@ module ElasticAPM
5
5
  module Spies
6
6
  # @api private
7
7
  class HTTPSpy
8
+ TYPE = 'ext'
9
+ SUBTYPE = 'http_rb'
10
+
8
11
  # rubocop:disable Metrics/MethodLength
9
12
  def install
10
13
  ::HTTP::Client.class_eval do
@@ -19,9 +22,13 @@ module ElasticAPM
19
22
  host = req.uri.host
20
23
 
21
24
  name = "#{method} #{host}"
22
- type = "ext.http_rb.#{method}"
23
25
 
24
- ElasticAPM.with_span name, type do |span|
26
+ ElasticAPM.with_span(
27
+ name,
28
+ TYPE,
29
+ subtype: SUBTYPE,
30
+ action: method
31
+ ) do |span|
25
32
  trace_context = span&.trace_context || transaction.trace_context
26
33
  req['Elastic-Apm-Traceparent'] = trace_context.to_header
27
34
  perform_without_apm(req, options)
@@ -14,7 +14,9 @@ module ElasticAPM
14
14
 
15
15
  # @api private
16
16
  class Subscriber
17
- TYPE = 'db.mongodb.query'
17
+ TYPE = 'db'
18
+ SUBTYPE = 'mongodb'
19
+ ACTION = 'query'
18
20
 
19
21
  def initialize
20
22
  @events = {}
@@ -51,6 +53,8 @@ module ElasticAPM
51
53
  ElasticAPM.start_span(
52
54
  name,
53
55
  TYPE,
56
+ subtype: SUBTYPE,
57
+ action: ACTION,
54
58
  context: build_context(event)
55
59
  )
56
60
 
@@ -6,6 +6,8 @@ module ElasticAPM
6
6
  # @api private
7
7
  class NetHTTPSpy
8
8
  KEY = :__elastic_apm_net_http_disabled
9
+ TYPE = 'ext'
10
+ SUBTYPE = 'net_http'
9
11
 
10
12
  # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
11
13
  class << self
@@ -46,9 +48,13 @@ module ElasticAPM
46
48
  host ||= address
47
49
 
48
50
  name = "#{method} #{host}"
49
- type = "ext.net_http.#{method}"
50
51
 
51
- ElasticAPM.with_span name, type do |span|
52
+ ElasticAPM.with_span(
53
+ name,
54
+ TYPE,
55
+ subtype: SUBTYPE,
56
+ action: method.to_s
57
+ ) do |span|
52
58
  trace_context = span&.trace_context || transaction.trace_context
53
59
  req['Elastic-Apm-Traceparent'] = trace_context.to_header
54
60
  request_without_apm(req, body, &block)
@@ -29,8 +29,8 @@ module ElasticAPM
29
29
 
30
30
  Notification = Struct.new(:id, :span)
31
31
 
32
+ # rubocop:disable Metrics/MethodLength
32
33
  def start(name, id, payload)
33
- # debug "AS::Notification#start:#{name}:#{id}"
34
34
  return unless (transaction = @agent.current_transaction)
35
35
 
36
36
  normalized = @normalizers.normalize(transaction, name, payload)
@@ -39,12 +39,20 @@ module ElasticAPM
39
39
  if normalized == :skip
40
40
  nil
41
41
  else
42
- name, type, context = normalized
43
- @agent.start_span(name, type, context: context)
42
+ name, type, subtype, action, context = normalized
43
+
44
+ @agent.start_span(
45
+ name,
46
+ type,
47
+ subtype: subtype,
48
+ action: action,
49
+ context: context
50
+ )
44
51
  end
45
52
 
46
53
  transaction.notifications << Notification.new(id, span)
47
54
  end
55
+ # rubocop:enable Metrics/MethodLength
48
56
 
49
57
  def finish(_name, id, _payload)
50
58
  # debug "AS::Notification#finish:#{name}:#{id}"
@@ -13,7 +13,7 @@ module ElasticAPM
13
13
 
14
14
  attr_reader :context_serializer
15
15
 
16
- # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
16
+ # rubocop:disable Metrics/MethodLength
17
17
  def build(span)
18
18
  {
19
19
  span: {
@@ -21,7 +21,7 @@ module ElasticAPM
21
21
  transaction_id: span.transaction_id,
22
22
  parent_id: span.parent_id,
23
23
  name: keyword_field(span.name),
24
- type: keyword_field(span.type),
24
+ type: join_type(span),
25
25
  duration: ms(span.duration),
26
26
  context: context_serializer.build(span.context),
27
27
  stacktrace: span.stacktrace.to_a,
@@ -30,7 +30,7 @@ module ElasticAPM
30
30
  }
31
31
  }
32
32
  end
33
- # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
33
+ # rubocop:enable Metrics/MethodLength
34
34
 
35
35
  # @api private
36
36
  class ContextSerializer < Serializer
@@ -66,6 +66,14 @@ module ElasticAPM
66
66
  }
67
67
  end
68
68
  end
69
+
70
+ private
71
+
72
+ def join_type(span)
73
+ combined = [span.type, span.subtype, span.action]
74
+ combined.compact!
75
+ combined.join '.'
76
+ end
69
77
  end
70
78
  end
71
79
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ElasticAPM
4
- VERSION = '2.10.1'
4
+ VERSION = '2.11.0'
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: 2.10.1
4
+ version: 2.11.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: 2019-09-16 00:00:00.000000000 Z
11
+ date: 2019-09-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -59,6 +59,7 @@ files:
59
59
  - ".ci/jobs/apm-agent-ruby.yml"
60
60
  - ".ci/jobs/defaults.yml"
61
61
  - ".ci/linting.groovy"
62
+ - ".ci/prepare-git-context.sh"
62
63
  - ".gitignore"
63
64
  - ".hound.yml"
64
65
  - ".pre-commit-config.yaml"
@@ -92,6 +93,7 @@ files:
92
93
  - docs/getting-started-rails.asciidoc
93
94
  - docs/index.asciidoc
94
95
  - docs/introduction.asciidoc
96
+ - docs/log-correlation.asciidoc
95
97
  - docs/metrics.asciidoc
96
98
  - docs/opentracing.asciidoc
97
99
  - docs/release-notes.asciidoc
@@ -139,6 +141,7 @@ files:
139
141
  - lib/elastic_apm/normalizers/action_view.rb
140
142
  - lib/elastic_apm/normalizers/active_record.rb
141
143
  - lib/elastic_apm/opentracing.rb
144
+ - lib/elastic_apm/rails.rb
142
145
  - lib/elastic_apm/railtie.rb
143
146
  - lib/elastic_apm/span.rb
144
147
  - lib/elastic_apm/span/context.rb
@@ -206,7 +209,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
206
209
  - !ruby/object:Gem::Version
207
210
  version: '0'
208
211
  requirements: []
209
- rubygems_version: 3.0.6
212
+ rubygems_version: 3.0.3
210
213
  signing_key:
211
214
  specification_version: 4
212
215
  summary: The official Elastic APM agent for Ruby