elastic-apm 4.5.1 → 4.6.1

Sign up to get free protection for your applications and to get access to all the features.
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