elastic-apm 4.5.1 → 4.6.1

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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.ci/.jenkins_exclude.yml +73 -16
  3. data/.ci/.jenkins_framework.yml +3 -2
  4. data/.ci/.jenkins_ruby.yml +1 -1
  5. data/.ci/.jenkins_ruby_benchmarks.yml +6 -0
  6. data/.ci/Jenkinsfile +35 -26
  7. data/.ci/docker/jruby/11-jdk/Dockerfile +3 -0
  8. data/.ci/docker/jruby/12-jdk/Dockerfile +3 -0
  9. data/.ci/docker/jruby/13-jdk/Dockerfile +3 -0
  10. data/.ci/docker/jruby/7-jdk/Dockerfile +3 -0
  11. data/.ci/docker/jruby/8-jdk/Dockerfile +3 -0
  12. data/.ci/snapshoty.yml +34 -0
  13. data/.ci/update-specs.yml +108 -0
  14. data/.github/workflows/update-specs.yml +30 -0
  15. data/CHANGELOG.asciidoc +34 -0
  16. data/Gemfile +10 -3
  17. data/bench/report.rb +1 -1
  18. data/docker-compose.yml +6 -0
  19. data/docs/configuration.asciidoc +6 -4
  20. data/elastic-apm.gemspec +3 -2
  21. data/lib/elastic_apm/central_config.rb +5 -0
  22. data/lib/elastic_apm/config/server_info.rb +50 -0
  23. data/lib/elastic_apm/config.rb +25 -4
  24. data/lib/elastic_apm/error.rb +2 -1
  25. data/lib/elastic_apm/error_builder.rb +1 -0
  26. data/lib/elastic_apm/instrumenter.rb +4 -2
  27. data/lib/elastic_apm/metadata/system_info/container_info.rb +4 -3
  28. data/lib/elastic_apm/metadata/system_info.rb +1 -1
  29. data/lib/elastic_apm/span/context/links.rb +32 -0
  30. data/lib/elastic_apm/span/context/service.rb +55 -0
  31. data/lib/elastic_apm/span/context.rb +19 -3
  32. data/lib/elastic_apm/span.rb +3 -0
  33. data/lib/elastic_apm/span_helpers.rb +2 -2
  34. data/lib/elastic_apm/spies/faraday.rb +23 -0
  35. data/lib/elastic_apm/spies/racecar.rb +77 -0
  36. data/lib/elastic_apm/spies/redis.rb +1 -1
  37. data/lib/elastic_apm/spies/sequel.rb +9 -0
  38. data/lib/elastic_apm/spies/sqs.rb +1 -0
  39. data/lib/elastic_apm/trace_context/tracestate.rb +4 -2
  40. data/lib/elastic_apm/trace_context.rb +1 -1
  41. data/lib/elastic_apm/transport/connection/http.rb +9 -3
  42. data/lib/elastic_apm/transport/serializers/span_serializer.rb +25 -0
  43. data/lib/elastic_apm/version.rb +1 -1
  44. data/lib/elastic_apm.rb +1 -0
  45. metadata +26 -3
@@ -506,12 +506,12 @@ Elastic APM can instrument your Rake tasks. This is an opt-in field, as they are
506
506
 
507
507
  [float]
508
508
  [[config-log-ecs-formatting]]
509
- ==== `log_ecs_formatting`
509
+ ==== `log_ecs_reformatting`
510
510
 
511
511
  [options="header"]
512
512
  |============
513
513
  | Environment | `Config` key | Default
514
- | `ELASTIC_APM_LOG_ECS_FORMATTING` | `log_ecs_formatting` | `off`
514
+ | `ELASTIC_APM_LOG_ECS_REFORMATTING` | `log_ecs_reformatting` | `off`
515
515
  |============
516
516
 
517
517
  This is an experimental option that configures the agent to use the logger from the `ecs-logging` gem. The two
@@ -520,7 +520,7 @@ valid options are `off` and `override`.
520
520
  Setting this option to `override` will set the agent logger to a `EcsLogging::Logger` instance and all logged output
521
521
  will be in ECS-compatible json.
522
522
 
523
- The `ecs-logging` gem must be installed before the agent is started. If `log_ecs_formatting` is set to `override`,
523
+ The `ecs-logging` gem must be installed before the agent is started. If `log_ecs_reformatting` is set to `override`,
524
524
  the agent will attempt to require the gem and if it cannot be loaded, it will fall back to using the standard Ruby
525
525
  `::Logger` and log the load error.
526
526
 
@@ -556,6 +556,8 @@ A path to a log file.
556
556
 
557
557
  By default Elastic APM logs to `stdout` or uses `Rails.log` when used with Rails.
558
558
 
559
+ If `log_path` is specified, this will override `Rails.log` to point to that path instead.
560
+
559
561
  This should support both absolute and relative paths. Please be sure the directory exists.
560
562
 
561
563
  [float]
@@ -654,7 +656,7 @@ when this option is false.
654
656
  [options="header"]
655
657
  |============
656
658
  | Environment | `Config` key | Default | Example
657
- | `ELASTIC_APM_SANITIZE_FIELD_NAMES` | `sanitize_field_names` | `"password,passwd,pwd,secret,*key,*token*,*session*,*credit*,*card*,authorization,set-cookie"` | `Auth*tion,abc*,*xyz`
659
+ | `ELASTIC_APM_SANITIZE_FIELD_NAMES` | `sanitize_field_names` | `"password,passwd,pwd,secret,*key,*token*,*session*,*credit*,*card*,*auth*,set-cookie"` | `Auth*tion,abc*,*xyz`
658
660
  |============
659
661
 
660
662
  Sometimes it is necessary to sanitize the data sent to Elastic APM to remove sensitive values.
data/elastic-apm.gemspec CHANGED
@@ -22,8 +22,8 @@ require 'elastic_apm/version'
22
22
  Gem::Specification.new do |spec|
23
23
  spec.name = 'elastic-apm'
24
24
  spec.version = ElasticAPM::VERSION
25
- spec.authors = ['Mikkel Malmberg']
26
- spec.email = ['mikkel@elastic.co']
25
+ spec.authors = ['Mikkel Malmberg', 'Emily Stolfo']
26
+ spec.email = ['info@elastic.co']
27
27
 
28
28
  spec.summary = 'The official Elastic APM agent for Ruby'
29
29
  spec.homepage = 'https://github.com/elastic/apm-agent-ruby'
@@ -37,6 +37,7 @@ Gem::Specification.new do |spec|
37
37
 
38
38
  spec.add_dependency('concurrent-ruby', '~> 1.0')
39
39
  spec.add_dependency('http', '>= 3.0')
40
+ spec.add_runtime_dependency('ruby2_keywords')
40
41
 
41
42
  spec.require_paths = ['lib']
42
43
  end
@@ -181,6 +181,11 @@ module ElasticAPM
181
181
  DEFAULT_MAX_AGE
182
182
  end
183
183
 
184
+ if seconds < 5
185
+ debug "Next fetch is too low (#{seconds}s) - increasing to default"
186
+ seconds = 5
187
+ end
188
+
184
189
  @scheduled_task =
185
190
  Concurrent::ScheduledTask
186
191
  .execute(seconds) { fetch_and_apply_config }
@@ -0,0 +1,50 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ # frozen_string_literal: true
19
+
20
+ module ElasticAPM
21
+ class Config
22
+ # @api private
23
+ class ServerInfo
24
+ attr_reader :payload, :config, :http
25
+
26
+ VERSION_8_0 = '8.0'
27
+ VERSION_0 = '0'
28
+
29
+ def initialize(config)
30
+ @config = config
31
+ @http = Transport::Connection::Http.new(config)
32
+ end
33
+
34
+ def execute
35
+ resp = http.get(config.server_url)
36
+ @payload = JSON.parse(resp.body)
37
+ rescue
38
+ @payload = { "version" => VERSION_0 }
39
+ end
40
+
41
+ def version
42
+ @version ||= begin
43
+ execute
44
+ payload["version"] ? payload["version"].to_s : VERSION_0
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+
@@ -24,14 +24,17 @@ require 'elastic_apm/config/options'
24
24
  require 'elastic_apm/config/round_float'
25
25
  require 'elastic_apm/config/regexp_list'
26
26
  require 'elastic_apm/config/wildcard_pattern_list'
27
+ require 'elastic_apm/deprecations'
28
+ require 'elastic_apm/config/server_info'
27
29
 
28
30
  module ElasticAPM
29
31
  # @api private
30
32
  class Config
31
33
  extend Options
34
+ extend Deprecations
32
35
 
33
36
  SANITIZE_FIELD_NAMES_DEFAULT =
34
- %w[password passwd pwd secret *key *token* *session* *credit* *card* authorization set-cookie].freeze
37
+ %w[password passwd pwd secret *key *token* *session* *credit* *card* *auth* set-cookie].freeze
35
38
 
36
39
  # rubocop:disable Layout/LineLength, Layout/ExtraSpacing
37
40
  option :config_file, type: :string, default: 'config/elastic_apm.yml'
@@ -70,7 +73,7 @@ module ElasticAPM
70
73
  option :ignore_url_patterns, type: :list, default: [], converter: RegexpList.new
71
74
  option :instrument, type: :bool, default: true
72
75
  option :instrumented_rake_tasks, type: :list, default: []
73
- option :log_ecs_formatting, type: :string, default: 'off'
76
+ option :log_ecs_reformatting, type: :string, default: 'off'
74
77
  option :log_level, type: :int, default: Logger::INFO, converter: LogLevelMap.new
75
78
  option :log_path, type: :string
76
79
  option :metrics_interval, type: :int, default: '30s', converter: Duration.new
@@ -98,6 +101,16 @@ module ElasticAPM
98
101
  option :use_elastic_traceparent_header, type: :bool, default: true
99
102
  option :verify_server_cert, type: :bool, default: true
100
103
 
104
+ def log_ecs_formatting
105
+ log_ecs_reformatting
106
+ end
107
+
108
+ def log_ecs_formatting=(value)
109
+ @options[:log_ecs_reformatting].set(value)
110
+ end
111
+
112
+ deprecate :log_ecs_formatting, :log_ecs_reformatting
113
+
101
114
  # rubocop:enable Layout/LineLength, Layout/ExtraSpacing
102
115
  def initialize(options = {})
103
116
  @options = load_schema
@@ -116,7 +129,9 @@ module ElasticAPM
116
129
 
117
130
  yield self if block_given?
118
131
 
119
- self.logger ||= build_logger
132
+ if self.logger.nil? || self.log_path
133
+ self.logger = build_logger
134
+ end
120
135
 
121
136
  @__view_paths ||= []
122
137
  @__root_path ||= Dir.pwd
@@ -144,6 +159,7 @@ module ElasticAPM
144
159
  mongo
145
160
  net_http
146
161
  rake
162
+ racecar
147
163
  redis
148
164
  resque
149
165
  s3
@@ -229,6 +245,10 @@ module ElasticAPM
229
245
  super.split.first + '>'
230
246
  end
231
247
 
248
+ def version
249
+ @version ||= ServerInfo.new(self).version
250
+ end
251
+
232
252
  private
233
253
 
234
254
  def load_config_file
@@ -247,7 +267,7 @@ module ElasticAPM
247
267
  end
248
268
 
249
269
  def build_logger
250
- if self.log_ecs_formatting == 'override'
270
+ if self.log_ecs_reformatting == 'override'
251
271
  begin
252
272
  return build_ecs_logger
253
273
  rescue LoadError
@@ -294,6 +314,7 @@ module ElasticAPM
294
314
  self.framework_name ||= 'Ruby on Rails'
295
315
  self.framework_version ||= ::Rails::VERSION::STRING
296
316
  self.logger ||= ::Rails.logger
317
+ self.log_level ||= ::Rails.logger.log_level
297
318
 
298
319
  self.__root_path = ::Rails.root.to_s
299
320
  self.__view_paths = app.config.paths['app/views'].existent +
@@ -33,7 +33,7 @@ module ElasticAPM
33
33
  end
34
34
 
35
35
  attr_accessor :id, :culprit, :exception, :log, :transaction_id,
36
- :transaction, :context, :parent_id, :trace_id
36
+ :transaction_name, :transaction, :context, :parent_id, :trace_id
37
37
  attr_reader :timestamp
38
38
 
39
39
  def inspect
@@ -41,6 +41,7 @@ module ElasticAPM
41
41
  " culprit:#{culprit}" \
42
42
  " timestamp:#{timestamp}" \
43
43
  " transaction_id:#{transaction_id}" \
44
+ " transaction_name:#{transaction_name}" \
44
45
  " trace_id:#{trace_id}" \
45
46
  " exception:#{exception.inspect}" \
46
47
  '>'
@@ -74,6 +74,7 @@ module ElasticAPM
74
74
  return unless transaction
75
75
 
76
76
  error.transaction_id = transaction.id
77
+ error.transaction_name = transaction.name
77
78
  error.transaction = {
78
79
  sampled: transaction.sampled?,
79
80
  type: transaction.type
@@ -124,7 +124,7 @@ module ElasticAPM
124
124
  sample_rate = trace_context.tracestate.sample_rate
125
125
  else
126
126
  sampled = random_sample?(config)
127
- sample_rate = config.transaction_sample_rate
127
+ sample_rate = sampled ? config.transaction_sample_rate : 0
128
128
  end
129
129
 
130
130
  transaction =
@@ -150,7 +150,9 @@ module ElasticAPM
150
150
 
151
151
  transaction.done result
152
152
 
153
- enqueue.call transaction
153
+ if transaction.sampled? || @config.version < Config::ServerInfo::VERSION_8_0
154
+ enqueue.call transaction
155
+ end
154
156
 
155
157
  update_transaction_metrics(transaction)
156
158
 
@@ -33,14 +33,15 @@ module ElasticAPM
33
33
 
34
34
  attr_reader :cgroup_path
35
35
 
36
- def read!
36
+ def read!(hostname)
37
37
  read_from_cgroup!
38
+ self.kubernetes_pod_name = hostname if kubernetes_pod_uid
38
39
  read_from_env!
39
40
  self
40
41
  end
41
42
 
42
- def self.read!
43
- new.read!
43
+ def self.read!(hostname)
44
+ new.read!(hostname)
44
45
  end
45
46
 
46
47
  def container
@@ -29,7 +29,7 @@ module ElasticAPM
29
29
  @architecture = gem_platform.cpu
30
30
  @platform = gem_platform.os
31
31
 
32
- container_info = ContainerInfo.read!
32
+ container_info = ContainerInfo.read!(@detected_hostname)
33
33
  @container = container_info.container
34
34
  @kubernetes = container_info.kubernetes
35
35
  end
@@ -0,0 +1,32 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ # frozen_string_literal: true
19
+
20
+ module ElasticAPM
21
+ class Span
22
+ class Context
23
+ # @api private
24
+ class Links < Array
25
+ # @api private
26
+ class Link < Struct.new(:trace_id, :span_id)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+
@@ -0,0 +1,55 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ # frozen_string_literal: true
19
+
20
+ module ElasticAPM
21
+ class Span
22
+ class Context
23
+ # @api private
24
+ class Service
25
+ include Fields
26
+
27
+ field :target
28
+
29
+ # @api private
30
+ class Target
31
+ include Fields
32
+
33
+ field :name, default: ''
34
+ field :type, default: ''
35
+ end
36
+
37
+ def initialize(target: nil, **attrs)
38
+ super(**attrs)
39
+
40
+ self.target = build_target(target)
41
+ end
42
+
43
+ private
44
+
45
+ def build_target(target = nil)
46
+ return Target.new unless target
47
+ return target if target.is_a?(Target)
48
+
49
+ Target.new(**target)
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+
@@ -27,7 +27,9 @@ module ElasticAPM
27
27
  http: nil,
28
28
  labels: {},
29
29
  sync: nil,
30
- message: nil
30
+ message: nil,
31
+ service: nil,
32
+ links: nil
31
33
  )
32
34
  @sync = sync
33
35
  @db = db && Db.new(**db)
@@ -43,6 +45,16 @@ module ElasticAPM
43
45
  when Hash then Message.new(**message)
44
46
  end
45
47
  @labels = labels
48
+ @service =
49
+ case service
50
+ when Service then service
51
+ when Hash then Service.new(**service)
52
+ end
53
+ @links =
54
+ case links
55
+ when Links then links
56
+ when Array then Links.new(links)
57
+ end
46
58
  end
47
59
 
48
60
  attr_reader(
@@ -50,10 +62,11 @@ module ElasticAPM
50
62
  :http,
51
63
  :labels,
52
64
  :sync,
53
- :message
65
+ :message,
66
+ :links
54
67
  )
55
68
 
56
- attr_accessor :destination
69
+ attr_accessor :destination, :service
57
70
  end
58
71
  end
59
72
  end
@@ -62,3 +75,6 @@ require 'elastic_apm/span/context/db'
62
75
  require 'elastic_apm/span/context/http'
63
76
  require 'elastic_apm/span/context/destination'
64
77
  require 'elastic_apm/span/context/message'
78
+ require 'elastic_apm/span/context/service'
79
+ require 'elastic_apm/span/context/links'
80
+
@@ -145,6 +145,9 @@ module ElasticAPM
145
145
  service: service,
146
146
  cloud: cloud
147
147
  )
148
+ context.service = Span::Context::Service.new(
149
+ target: Span::Context::Service::Target.new(name: context.destination.service.name, type: context.destination.service.type )
150
+ )
148
151
  end
149
152
 
150
153
  def inspect
@@ -37,7 +37,7 @@ module ElasticAPM
37
37
  type ||= Span::DEFAULT_TYPE
38
38
 
39
39
  klass.prepend(Module.new do
40
- define_method(method) do |*args, &block|
40
+ ruby2_keywords(define_method(method) do |*args, &block|
41
41
  unless ElasticAPM.current_transaction
42
42
  return super(*args, &block)
43
43
  end
@@ -45,7 +45,7 @@ module ElasticAPM
45
45
  ElasticAPM.with_span name.to_s, type.to_s do
46
46
  super(*args, &block)
47
47
  end
48
- end
48
+ end)
49
49
  end)
50
50
  end
51
51
  end
@@ -102,6 +102,7 @@ module ElasticAPM
102
102
  ) do |span|
103
103
  ElasticAPM::Spies.without_net_http do
104
104
  trace_context = span&.trace_context || transaction.trace_context
105
+ self.response :elastic_apm_middleware, span # middleware
105
106
 
106
107
  result = super(method, url, body, headers) do |req|
107
108
  trace_context.apply_headers { |k, v| req[k] = v }
@@ -122,6 +123,28 @@ module ElasticAPM
122
123
  # rubocop:enable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
123
124
 
124
125
  def install
126
+ # This middleware class has to be defined here because it inherits from
127
+ # ::Faraday::Middleware, which isn't defined when this file loads.
128
+ tracing_middleware = Class.new ::Faraday::Middleware do
129
+ attr_reader :span
130
+
131
+ def initialize(app, span = nil, options = {})
132
+ super(app)
133
+ @span = span
134
+ end
135
+
136
+ def on_complete(env)
137
+ status = env[:status]
138
+ http = span&.context&.http
139
+ if http && status
140
+ http.status_code = status.to_s
141
+ span.outcome = Span::Outcome.from_http_status(status)
142
+ end
143
+ end
144
+ end
145
+ ::Faraday::Response.register_middleware(
146
+ elastic_apm_middleware: -> { tracing_middleware }
147
+ )
125
148
  ::Faraday::Connection.prepend(Ext)
126
149
  end
127
150
  end
@@ -0,0 +1,77 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+
18
+ begin
19
+ require 'active_support/notifications'
20
+ require 'active_support/subscriber'
21
+
22
+ # frozen_string_literal: true
23
+ module ElasticAPM
24
+ # @api private
25
+ module Spies
26
+ # @api private
27
+ class RacecarSpy
28
+ TYPE = 'kafka'
29
+ SUBTYPE = 'racecar'
30
+
31
+ # @api private
32
+ class ConsumerSubscriber < ActiveSupport::Subscriber
33
+ def start_process_message(event)
34
+ start_process_transaction(event: event, kind: 'process_message')
35
+ end
36
+ def process_message(_event)
37
+ ElasticAPM.end_transaction
38
+ end
39
+
40
+ def start_process_batch(event)
41
+ start_process_transaction(event: event, kind: 'process_batch')
42
+ end
43
+ def process_batch(_event)
44
+ ElasticAPM.end_transaction
45
+ end
46
+
47
+ private # only public methods will be subscribed
48
+
49
+ def start_process_transaction(event:, kind:)
50
+ ElasticAPM.start_transaction(kind, TYPE)
51
+ ElasticAPM.current_transaction.context.set_service(framework_name: 'racecar', framework_version: Racecar::VERSION)
52
+ end
53
+ end
54
+
55
+ class ProducerSubscriber < ActiveSupport::Subscriber
56
+ def start_deliver_message(event)
57
+ ElasticAPM.start_transaction('deliver_message',TYPE)
58
+ ElasticAPM.current_transaction.context.set_service(framework_name: 'racecar', framework_version: Racecar::VERSION)
59
+ end
60
+
61
+ def deliver_message(_event)
62
+ ElasticAPM.end_transaction
63
+ end
64
+ end
65
+
66
+ def install
67
+ ConsumerSubscriber.attach_to(:racecar)
68
+ ProducerSubscriber.attach_to(:racecar)
69
+ end
70
+ end
71
+ register 'Racecar', 'racecar', RacecarSpy.new
72
+ end
73
+ end
74
+
75
+ rescue LoadError
76
+ # no active support available
77
+ end
@@ -25,7 +25,7 @@ module ElasticAPM
25
25
  # @api private
26
26
  module Ext
27
27
  def call(command, &block)
28
- name = command[0].upcase
28
+ name = command[0].to_s.upcase
29
29
 
30
30
  return super(command, &block) if command[0] == :auth
31
31
 
@@ -44,8 +44,17 @@ module ElasticAPM
44
44
  name =
45
45
  ElasticAPM::Spies::SequelSpy.summarizer.summarize sql
46
46
 
47
+ db_name = ''
48
+ # postgresql shows current database
49
+ db_name = connection&.db.to_s if connection.respond_to?(:db)
50
+ # sqlite may expose a filename
51
+ db_name = connection&.filename.to_s if db_name == '' && connection.respond_to?(:filename)
52
+ # fall back to adapter class name
53
+ db_name = connection.class.to_s if db_name == ''
54
+
47
55
  context = ElasticAPM::Span::Context.new(
48
56
  db: { statement: sql, type: 'sql', user: opts[:user] },
57
+ service: {target: {type: subtype, name: db_name }},
49
58
  destination: { service: { resource: subtype } }
50
59
  )
51
60
 
@@ -56,6 +56,7 @@ module ElasticAPM
56
56
  service: { resource: "#{SUBTYPE}/#{queue_name}" },
57
57
  cloud: { region: region }
58
58
  }
59
+ # span links added here?
59
60
  )
60
61
  end
61
62
 
@@ -92,6 +92,8 @@ module ElasticAPM
92
92
 
93
93
  extend Forwardable
94
94
 
95
+ ENTRY_SPLIT_REGEX = /\s*[\n,]+\s*/
96
+
95
97
  def initialize(entries: {}, sample_rate: nil)
96
98
  @entries = entries
97
99
 
@@ -107,7 +109,7 @@ module ElasticAPM
107
109
  split_by_nl_and_comma(header)
108
110
  .each_with_object({}) do |entry, hsh|
109
111
  k, v = entry.split('=')
110
-
112
+ next unless k && v && !k.empty? && !v.empty?
111
113
  hsh[k] =
112
114
  case k
113
115
  when 'es' then EsEntry.new(v)
@@ -140,7 +142,7 @@ module ElasticAPM
140
142
  # Rack handles this by joining the headers under the same key,
141
143
  # separated by newlines.
142
144
  # See https://www.rubydoc.info/github/rack/rack/file/SPEC
143
- String(str).split("\n").map { |s| s.split(',') }.flatten
145
+ String(str).split(ENTRY_SPLIT_REGEX).flatten
144
146
  end
145
147
  end
146
148
  end
@@ -96,7 +96,7 @@ module ElasticAPM
96
96
  def apply_headers
97
97
  yield 'Traceparent', traceparent.to_header
98
98
 
99
- if tracestate
99
+ if tracestate && !tracestate.to_header.empty?
100
100
  yield 'Tracestate', tracestate.to_header
101
101
  end
102
102
 
@@ -75,7 +75,15 @@ module ElasticAPM
75
75
  @closed.make_true
76
76
 
77
77
  @wr&.close
78
- return if @request.nil? || @request&.join(5)
78
+
79
+ if @request&.join(5)
80
+ @rd&.close
81
+ return
82
+ end
83
+
84
+ @rd&.close
85
+
86
+ return if @request.nil?
79
87
 
80
88
  error(
81
89
  '%s: APM Server not responding in time, terminating request',
@@ -117,8 +125,6 @@ module ElasticAPM
117
125
  error(
118
126
  "Couldn't establish connection to APM Server:\n%p", e.inspect
119
127
  )
120
- ensure
121
- @rd&.close
122
128
  end
123
129
  end
124
130
  end