elastic-apm 3.14.0 → 3.15.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f35288b7b9e691e200b5ae0183f6cc81ba40cb3cf520f11e3319e6a31461b8d2
4
- data.tar.gz: '0487ec48ab081691b85d410711402a9777e9a2c815e5384148d2f33ee0d48e89'
3
+ metadata.gz: ee845be05bcd6dbd56efb3f21372b0abfede8c37b4c7f9580b82ea6a24238fc5
4
+ data.tar.gz: 46365fa74214843bc882163c917b429beba61d35f65032f45321ce0e128ecf38
5
5
  SHA512:
6
- metadata.gz: 17f18165f940d10a57a0fec94f1f9c5311e0d4c4769273f30a749c2512cc125f912f53b15b78f7a90bb5dc8e6c41dbdf7ecb7b9ecc669f581e0768e22d30771f
7
- data.tar.gz: 0d2e0655d61495157b947d1ccd441511ee1508ba32b7acbe00dc6deeebd343efa708c6755afa9fc8a9f150cda3224eac52f42f7841c6eeb704bf40bb85c68fb2
6
+ metadata.gz: 821287e4a80cdf707c6bcddb50ff1cf9592e5410c99e09b69dbebbcc4915a9aae968f8bc1704454ad06a964f173e49b8534a5923d059ef4a70417b74b2b285e8
7
+ data.tar.gz: 5344fb235b378cff3610c0e49771737ba4c906343025110eb38e277f0cb27a6419e76834baf275ebfb2d59ea8e4b94298b65ebe757dcd7fd9cd0d63727a4fe77
data/.rubocop.yml CHANGED
@@ -7,7 +7,7 @@ AllCops:
7
7
  - 'bench/*'
8
8
  - 'spec/support/helloworld_pb.rb'
9
9
  - 'spec/support/helloworld_services_pb.rb'
10
- - 'features/step_definitions/stepdefs.rb'
10
+ - 'features/**/*'
11
11
 
12
12
  require:
13
13
  - rubocop-performance
data/CHANGELOG.asciidoc CHANGED
@@ -35,12 +35,29 @@ endif::[]
35
35
  [[release-notes-3.x]]
36
36
  === Ruby Agent version 3.x
37
37
 
38
+ [[release-notes-3.15.0]]
39
+ ==== 3.15.0 (2021-03-22)
40
+
41
+ [float]
42
+ ===== Changed
43
+
44
+ - Changed DynamoDB instrumentation to match spec. Span names now follow the format `DynamoDB
45
+ Operation [table]` {pull}976[#976]
46
+
47
+ [float]
48
+ ===== Added
49
+ - Support for AWS S3 {pull}977[#977]
50
+ - Support for AWS SQS {pull}978[#978]
51
+ - Support for AWS SNS {pull}978[#978]
52
+
38
53
  [[release-notes-3.14.0]]
39
54
  ==== 3.14.0 (2021-03-17)
40
55
 
41
56
  [float]
42
57
  ===== Added
43
- - Expanded support for extracting ActiveRecord adapter name from notification payload when using ActiveRecord versions before 6.0 {pull}953[#953]
58
+
59
+ - Expanded support for extracting ActiveRecord adapter name from notification payload when using
60
+ ActiveRecord versions before 6.0 {pull}953[#953]
44
61
 
45
62
  [float]
46
63
  ===== Fixed
data/Gemfile CHANGED
@@ -31,7 +31,9 @@ gem 'webmock'
31
31
 
32
32
  # Integrations
33
33
  gem 'aws-sdk-dynamodb', require: nil
34
+ gem 'aws-sdk-s3', require: nil
34
35
  gem 'aws-sdk-sqs', require: nil
36
+ gem 'aws-sdk-sns', require: nil
35
37
  gem 'elasticsearch', require: nil
36
38
  gem 'fakeredis', require: nil
37
39
  gem 'faraday', require: nil
@@ -151,11 +151,14 @@ module ElasticAPM
151
151
  rake
152
152
  redis
153
153
  resque
154
+ s3
154
155
  sequel
155
156
  shoryuken
156
157
  sidekiq
157
158
  sinatra
158
159
  sneakers
160
+ sns
161
+ sqs
159
162
  sucker_punch
160
163
  tilt
161
164
  ]
@@ -18,6 +18,7 @@
18
18
  # frozen_string_literal: true
19
19
 
20
20
  module ElasticAPM
21
+ # TODO: Remove this?
21
22
  # @api private
22
23
  module NaivelyHashable
23
24
  def naively_hashable?
@@ -57,12 +57,16 @@ module ElasticAPM
57
57
  private
58
58
 
59
59
  def subtype_for(payload)
60
- return cached_adapter_name(payload[:connection].adapter_name) if payload[:connection]
60
+ if payload[:connection]
61
+ return cached_adapter_name(payload[:connection].adapter_name)
62
+ end
61
63
 
62
64
  if can_attempt_connection_id_lookup?(payload)
63
65
  begin
64
66
  loaded_object = ObjectSpace._id2ref(payload[:connection_id])
65
- return cached_adapter_name(loaded_object.adapter_name) if loaded_object.respond_to?(:adapter_name)
67
+ if loaded_object.respond_to?(:adapter_name)
68
+ return cached_adapter_name(loaded_object.adapter_name)
69
+ end
66
70
  rescue RangeError # if connection object has somehow been garbage collected
67
71
  end
68
72
  end
@@ -84,7 +88,9 @@ module ElasticAPM
84
88
  end
85
89
 
86
90
  def can_attempt_connection_id_lookup?(payload)
87
- RUBY_ENGINE == "ruby" && payload[:connection_id] && ObjectSpace.respond_to?(:_id2ref)
91
+ RUBY_ENGINE == "ruby" &&
92
+ payload[:connection_id] &&
93
+ ObjectSpace.respond_to?(:_id2ref)
88
94
  end
89
95
  end
90
96
  end
@@ -26,7 +26,8 @@ module ElasticAPM
26
26
  destination: nil,
27
27
  http: nil,
28
28
  labels: {},
29
- sync: nil
29
+ sync: nil,
30
+ message: nil
30
31
  )
31
32
  @sync = sync
32
33
  @db = db && Db.new(**db)
@@ -36,6 +37,11 @@ module ElasticAPM
36
37
  when Destination then destination
37
38
  when Hash then Destination.new(**destination)
38
39
  end
40
+ @message =
41
+ case message
42
+ when Message then message
43
+ when Hash then Message.new(**message)
44
+ end
39
45
  @labels = labels
40
46
  end
41
47
 
@@ -44,7 +50,8 @@ module ElasticAPM
44
50
  :destination,
45
51
  :http,
46
52
  :labels,
47
- :sync
53
+ :sync,
54
+ :message
48
55
  )
49
56
  end
50
57
  end
@@ -53,3 +60,4 @@ end
53
60
  require 'elastic_apm/span/context/db'
54
61
  require 'elastic_apm/span/context/http'
55
62
  require 'elastic_apm/span/context/destination'
63
+ require 'elastic_apm/span/context/message'
@@ -22,18 +22,30 @@ module ElasticAPM
22
22
  class Context
23
23
  # @api private
24
24
  class Destination
25
+
26
+ # @api private
27
+ class Cloud
28
+ def initialize(region: nil)
29
+ @region = region
30
+ end
31
+
32
+ attr_accessor :region
33
+ end
34
+
25
35
  def initialize(
26
36
  name: nil,
27
37
  resource: nil,
28
38
  type: nil,
29
39
  address: nil,
30
- port: nil
40
+ port: nil,
41
+ cloud: nil
31
42
  )
32
43
  @name = name
33
44
  @resource = resource
34
45
  @type = type
35
46
  @address = address
36
47
  @port = port
48
+ @cloud = cloud
37
49
  end
38
50
 
39
51
  attr_reader(
@@ -41,7 +53,8 @@ module ElasticAPM
41
53
  :resource,
42
54
  :type,
43
55
  :address,
44
- :port
56
+ :port,
57
+ :cloud
45
58
  )
46
59
 
47
60
  def self.from_uri(uri_or_str, type: 'external', port: nil)
@@ -0,0 +1,40 @@
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 Message
25
+ def initialize(
26
+ queue_name: nil,
27
+ age_ms: nil
28
+ )
29
+ @queue_name = queue_name
30
+ @age_ms = age_ms
31
+ end
32
+
33
+ attr_reader(
34
+ :queue_name,
35
+ :age_ms
36
+ )
37
+ end
38
+ end
39
+ end
40
+ end
@@ -22,6 +22,11 @@ module ElasticAPM
22
22
  module Spies
23
23
  # @api private
24
24
  class DynamoDBSpy
25
+ TYPE = 'db'
26
+ SUBTYPE = 'dynamodb'
27
+
28
+ @@formatted_op_names = Concurrent::Map.new
29
+
25
30
  def self.without_net_http
26
31
  return yield unless defined?(NetHTTPSpy)
27
32
 
@@ -32,6 +37,18 @@ module ElasticAPM
32
37
  # rubocop:enable Style/ExplicitBlockArgument
33
38
  end
34
39
 
40
+ def self.span_name(operation_name, params)
41
+ params[:table_name] ?
42
+ "DynamoDB #{formatted_op_name(operation_name)} #{params[:table_name]}" :
43
+ "DynamoDB #{formatted_op_name(operation_name)}"
44
+ end
45
+
46
+ def self.formatted_op_name(operation_name)
47
+ @@formatted_op_names.compute_if_absent(operation_name) do
48
+ operation_name.to_s.split('_').collect(&:capitalize).join
49
+ end
50
+ end
51
+
35
52
  def install
36
53
  ::Aws::DynamoDB::Client.class_eval do
37
54
  # Alias all available operations
@@ -39,11 +56,27 @@ module ElasticAPM
39
56
  alias :"#{operation_name}_without_apm" :"#{operation_name}"
40
57
 
41
58
  define_method(operation_name) do |params = {}, options = {}|
59
+ cloud = ElasticAPM::Span::Context::Destination::Cloud.new(region: config.region)
60
+
61
+ context = ElasticAPM::Span::Context.new(
62
+ db: {
63
+ instance: config.region,
64
+ type: SUBTYPE,
65
+ statement: params[:key_condition_expression]
66
+ },
67
+ destination: {
68
+ cloud: cloud,
69
+ resource: SUBTYPE,
70
+ type: TYPE
71
+ }
72
+ )
73
+
42
74
  ElasticAPM.with_span(
43
- operation_name,
44
- 'db',
45
- subtype: 'dynamodb',
46
- action: operation_name
75
+ ElasticAPM::Spies::DynamoDBSpy.span_name(operation_name, params),
76
+ TYPE,
77
+ subtype: SUBTYPE,
78
+ action: operation_name,
79
+ context: context
47
80
  ) do
48
81
  ElasticAPM::Spies::DynamoDBSpy.without_net_http do
49
82
  original_method = method("#{operation_name}_without_apm")
@@ -0,0 +1,127 @@
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
+ # @api private
22
+ module Spies
23
+ # @api private
24
+ class S3Spy
25
+ TYPE = 'storage'
26
+ SUBTYPE = 's3'
27
+ AP_REGION_REGEX = /^(?:[^:]+:){3}([^:]+).*/
28
+ AP_REGEX = /:accesspoint.*/
29
+ MUTEX = Mutex.new
30
+
31
+ @@formatted_op_names = {}
32
+
33
+ def self.without_net_http
34
+ return yield unless defined?(NetHTTPSpy)
35
+
36
+ # rubocop:disable Style/ExplicitBlockArgument
37
+ ElasticAPM::Spies::NetHTTPSpy.disable_in do
38
+ yield
39
+ end
40
+ # rubocop:enable Style/ExplicitBlockArgument
41
+ end
42
+
43
+ def self.bucket_name(params)
44
+ if params[:bucket]
45
+ if index = params[:bucket].rindex(AP_REGEX)
46
+ params[:bucket][index+1..-1]
47
+ else
48
+ params[:bucket]
49
+ end
50
+ end
51
+ end
52
+
53
+ def self.accesspoint_region(params)
54
+ if params[:bucket] && (match = AP_REGION_REGEX.match(params[:bucket]))
55
+ match[1]
56
+ end
57
+ end
58
+
59
+ def self.span_name(operation_name, bucket_name)
60
+ bucket_name ? "S3 #{formatted_op_name(operation_name)} #{bucket_name}" :
61
+ "S3 #{formatted_op_name(operation_name)}"
62
+ end
63
+
64
+ def self.formatted_op_name(operation_name)
65
+ if @@formatted_op_names[operation_name]
66
+ return @@formatted_op_names[operation_name]
67
+ end
68
+
69
+ MUTEX.synchronize do
70
+ if @@formatted_op_names[operation_name]
71
+ return @@formatted_op_names[operation_name]
72
+ end
73
+
74
+ @@formatted_op_names[operation_name] =
75
+ operation_name.to_s.split('_').collect(&:capitalize).join
76
+ end
77
+
78
+ @@formatted_op_names[operation_name]
79
+ end
80
+
81
+
82
+ def install
83
+ ::Aws::S3::Client.class_eval do
84
+ # Alias all available operations
85
+ api.operation_names.each do |operation_name|
86
+ alias :"#{operation_name}_without_apm" :"#{operation_name}"
87
+
88
+ define_method(operation_name) do |params = {}, options = {}|
89
+ bucket_name = ElasticAPM::Spies::S3Spy.bucket_name(params)
90
+ cloud = ElasticAPM::Span::Context::Destination::Cloud.new(
91
+ region: ElasticAPM::Spies::S3Spy.accesspoint_region(params) || config.region
92
+ )
93
+
94
+ context = ElasticAPM::Span::Context.new(
95
+ destination: {
96
+ cloud: cloud,
97
+ resource: bucket_name,
98
+ type: TYPE,
99
+ name: SUBTYPE
100
+ }
101
+ )
102
+
103
+ ElasticAPM.with_span(
104
+ ElasticAPM::Spies::S3Spy.span_name(operation_name, bucket_name),
105
+ TYPE,
106
+ subtype: SUBTYPE,
107
+ action: ElasticAPM::Spies::S3Spy.formatted_op_name(operation_name),
108
+ context: context
109
+ ) do
110
+ ElasticAPM::Spies::S3Spy.without_net_http do
111
+ original_method = method("#{operation_name}_without_apm")
112
+ original_method.call(params, options)
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
120
+
121
+ register(
122
+ 'Aws::S3::Client',
123
+ 'aws-sdk-s3',
124
+ S3Spy.new
125
+ )
126
+ end
127
+ end
@@ -0,0 +1,131 @@
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
+ # @api private
22
+ module Spies
23
+ # @api private
24
+ class SNSSpy
25
+ TYPE = 'messaging'
26
+ SUBTYPE = 'sns'
27
+ AP_REGEX = /:accesspoint[\/:].*/
28
+ AP_REGION_REGEX = /^(?:[^:]+:){3}([^:]+).*/
29
+
30
+ def self.without_net_http
31
+ return yield unless defined?(NetHTTPSpy)
32
+
33
+ # rubocop:disable Style/ExplicitBlockArgument
34
+ ElasticAPM::Spies::NetHTTPSpy.disable_in do
35
+ yield
36
+ end
37
+ # rubocop:enable Style/ExplicitBlockArgument
38
+ end
39
+
40
+ def self.get_topic(params)
41
+ return '<PHONE_NUMBER>' if params[:phone_number]
42
+
43
+ last_after_slash_or_colon(
44
+ params[:topic_arn] || params[:target_arn]
45
+ )
46
+ end
47
+
48
+ def self.last_after_slash_or_colon(arn)
49
+ if index = arn.rindex(AP_REGEX)
50
+ return arn[index+1..-1]
51
+ end
52
+
53
+ if arn.include?('/')
54
+ arn.split('/')[-1]
55
+ else
56
+ arn.split(':')[-1]
57
+ end
58
+ end
59
+
60
+ def self.arn_region(arn)
61
+ if arn && (match = AP_REGION_REGEX.match(arn))
62
+ match[1]
63
+ end
64
+ end
65
+
66
+ def self.span_context(topic, region)
67
+ cloud = ElasticAPM::Span::Context::Destination::Cloud.new(region: region)
68
+
69
+ ElasticAPM::Span::Context.new(
70
+ message: {
71
+ queue_name: topic
72
+ },
73
+ destination: {
74
+ resource: [SUBTYPE, topic].compact.join('/'),
75
+ type: TYPE,
76
+ name: SUBTYPE,
77
+ cloud: cloud
78
+ }
79
+ )
80
+ end
81
+
82
+ def install
83
+ ::Aws::SNS::Client.class_eval do
84
+ alias :publish_without_apm :publish
85
+
86
+ def publish(params = {}, options = {})
87
+ unless (transaction = ElasticAPM.current_transaction)
88
+ return publish_without_apm(params, options)
89
+ end
90
+
91
+ topic = ElasticAPM::Spies::SNSSpy.get_topic(params)
92
+ span_name = topic ? "SNS PUBLISH to #{topic}" : 'SNS PUBLISH'
93
+ region = ElasticAPM::Spies::SNSSpy.arn_region(
94
+ params[:topic_arn] || params[:target_arn]
95
+ )
96
+ context = ElasticAPM::Spies::SNSSpy.span_context(
97
+ topic,
98
+ region || config.region
99
+ )
100
+
101
+ ElasticAPM.with_span(
102
+ span_name,
103
+ TYPE,
104
+ subtype: SUBTYPE,
105
+ action: 'publish',
106
+ context: context
107
+ ) do |span|
108
+ trace_context = span&.trace_context || transaction.trace_context
109
+ trace_context.apply_headers do |key, value|
110
+ params[:message_attributes] ||= {}
111
+ params[:message_attributes][key] ||= {}
112
+ params[:message_attributes][key][:string_value] = value
113
+ params[:message_attributes][key][:data_type] = 'String'
114
+ end
115
+
116
+ ElasticAPM::Spies::SNSSpy.without_net_http do
117
+ publish_without_apm(params, options)
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
124
+
125
+ register(
126
+ 'Aws::SNS::Client',
127
+ 'aws-sdk-sns',
128
+ SNSSpy.new
129
+ )
130
+ end
131
+ end
@@ -0,0 +1,240 @@
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
+ # @api private
22
+ module Spies
23
+ # @api private
24
+ class SQSSpy
25
+ TYPE = 'messaging'
26
+ SUBTYPE = 'sqs'
27
+
28
+ REGION_REGEXP = %r{https://sqs\.([a-z0-9-]+)\.amazonaws}
29
+
30
+ def self.without_net_http
31
+ return yield unless defined?(NetHTTPSpy)
32
+
33
+ # rubocop:disable Style/ExplicitBlockArgument
34
+ ElasticAPM::Spies::NetHTTPSpy.disable_in do
35
+ yield
36
+ end
37
+ # rubocop:enable Style/ExplicitBlockArgument
38
+ end
39
+
40
+ def self.queue_name(params)
41
+ if params[:queue_url]
42
+ params[:queue_url].split('/')[-1]
43
+ end
44
+ end
45
+
46
+ def self.region_from_url(url)
47
+ if match = REGION_REGEXP.match(url)
48
+ match[1]
49
+ end
50
+ end
51
+
52
+ def self.span_context(queue_name, region)
53
+ cloud = ElasticAPM::Span::Context::Destination::Cloud.new(region: region)
54
+
55
+ ElasticAPM::Span::Context.new(
56
+ message: {
57
+ queue_name: queue_name
58
+ },
59
+ destination: {
60
+ resource: [SUBTYPE, queue_name].compact.join('/'),
61
+ type: TYPE,
62
+ name: SUBTYPE,
63
+ cloud: cloud
64
+ }
65
+ )
66
+ end
67
+
68
+ def install
69
+ ::Aws::SQS::Client.class_eval do
70
+ alias :send_message_without_apm :send_message
71
+
72
+ def send_message(params = {}, options = {})
73
+ unless (transaction = ElasticAPM.current_transaction)
74
+ return send_message_without_apm(params, options)
75
+ end
76
+
77
+ queue_name = ElasticAPM::Spies::SQSSpy.queue_name(params)
78
+ span_name = queue_name ? "SQS SEND to #{queue_name}" : 'SQS SEND'
79
+ region = ElasticAPM::Spies::SQSSpy.region_from_url(params[:queue_url])
80
+ context = ElasticAPM::Spies::SQSSpy.span_context(
81
+ queue_name,
82
+ region || config.region
83
+ )
84
+
85
+ ElasticAPM.with_span(
86
+ span_name,
87
+ TYPE,
88
+ subtype: SUBTYPE,
89
+ action: 'send',
90
+ context: context
91
+ ) do |span|
92
+ trace_context = span&.trace_context || transaction.trace_context
93
+ trace_context.apply_headers do |key, value|
94
+ params[:message_attributes] ||= {}
95
+ params[:message_attributes][key] ||= {}
96
+ params[:message_attributes][key][:string_value] = value
97
+ params[:message_attributes][key][:data_type] = 'String'
98
+ end
99
+
100
+ ElasticAPM::Spies::SQSSpy.without_net_http do
101
+ send_message_without_apm(params, options)
102
+ end
103
+ end
104
+ end
105
+
106
+ alias :send_message_batch_without_apm :send_message_batch
107
+
108
+ def send_message_batch(params = {}, options = {})
109
+ unless (transaction = ElasticAPM.current_transaction)
110
+ return send_message_batch_without_apm(params, options)
111
+ end
112
+
113
+ queue_name = ElasticAPM::Spies::SQSSpy.queue_name(params)
114
+ span_name =
115
+ queue_name ? "SQS SEND_BATCH to #{queue_name}" : 'SQS SEND_BATCH'
116
+ region = ElasticAPM::Spies::SQSSpy.region_from_url(params[:queue_url])
117
+ context = ElasticAPM::Spies::SQSSpy.span_context(
118
+ queue_name,
119
+ region || config.region
120
+ )
121
+
122
+ ElasticAPM.with_span(
123
+ span_name,
124
+ TYPE,
125
+ subtype: SUBTYPE,
126
+ action: 'send_batch',
127
+ context: context
128
+ ) do |span|
129
+ trace_context = span&.trace_context || transaction.trace_context
130
+ trace_context.apply_headers do |key, value|
131
+ params[:entries].each do |message|
132
+ message[:message_attributes] ||= {}
133
+ message[:message_attributes][key] ||= {}
134
+ message[:message_attributes][key][:string_value] = value
135
+ message[:message_attributes][key][:data_type] = 'String'
136
+ end
137
+ end
138
+
139
+ ElasticAPM::Spies::SQSSpy.without_net_http do
140
+ send_message_batch_without_apm(params, options)
141
+ end
142
+ end
143
+ end
144
+
145
+ alias :receive_message_without_apm :receive_message
146
+
147
+ def receive_message(params = {}, options = {})
148
+ unless ElasticAPM.current_transaction
149
+ return receive_message_without_apm(params, options)
150
+ end
151
+
152
+ queue_name = ElasticAPM::Spies::SQSSpy.queue_name(params)
153
+ span_name =
154
+ queue_name ? "SQS RECEIVE from #{queue_name}" : 'SQS RECEIVE'
155
+ region = ElasticAPM::Spies::SQSSpy.region_from_url(params[:queue_url])
156
+ context = ElasticAPM::Spies::SQSSpy.span_context(
157
+ queue_name,
158
+ region || config.region
159
+ )
160
+
161
+ ElasticAPM.with_span(
162
+ span_name,
163
+ TYPE,
164
+ subtype: SUBTYPE,
165
+ action: 'receive',
166
+ context: context
167
+ ) do
168
+ ElasticAPM::Spies::SQSSpy.without_net_http do
169
+ receive_message_without_apm(params, options)
170
+ end
171
+ end
172
+ end
173
+
174
+ alias :delete_message_without_apm :delete_message
175
+
176
+ def delete_message(params = {}, options = {})
177
+ unless ElasticAPM.current_transaction
178
+ return delete_message_without_apm(params, options)
179
+ end
180
+
181
+ queue_name = ElasticAPM::Spies::SQSSpy.queue_name(params)
182
+ span_name = queue_name ? "SQS DELETE from #{queue_name}" : 'SQS DELETE'
183
+ region = ElasticAPM::Spies::SQSSpy.region_from_url(params[:queue_url])
184
+ context = ElasticAPM::Spies::SQSSpy.span_context(
185
+ queue_name,
186
+ region || config.region
187
+ )
188
+
189
+ ElasticAPM.with_span(
190
+ span_name,
191
+ TYPE,
192
+ subtype: SUBTYPE,
193
+ action: 'delete',
194
+ context: context
195
+ ) do
196
+ ElasticAPM::Spies::SQSSpy.without_net_http do
197
+ delete_message_without_apm(params, options)
198
+ end
199
+ end
200
+ end
201
+
202
+ alias :delete_message_batch_without_apm :delete_message_batch
203
+
204
+ def delete_message_batch(params = {}, options = {})
205
+ unless ElasticAPM.current_transaction
206
+ return delete_message_batch_without_apm(params, options)
207
+ end
208
+
209
+ queue_name = ElasticAPM::Spies::SQSSpy.queue_name(params)
210
+ span_name =
211
+ queue_name ? "SQS DELETE_BATCH from #{queue_name}" : 'SQS DELETE_BATCH'
212
+ region = ElasticAPM::Spies::SQSSpy.region_from_url(params[:queue_url])
213
+ context = ElasticAPM::Spies::SQSSpy.span_context(
214
+ queue_name,
215
+ region || config.region
216
+ )
217
+
218
+ ElasticAPM.with_span(
219
+ span_name,
220
+ TYPE,
221
+ subtype: SUBTYPE,
222
+ action: 'delete_batch',
223
+ context: context
224
+ ) do
225
+ ElasticAPM::Spies::SQSSpy.without_net_http do
226
+ delete_message_batch_without_apm(params, options)
227
+ end
228
+ end
229
+ end
230
+ end
231
+ end
232
+ end
233
+
234
+ register(
235
+ 'Aws::SQS::Client',
236
+ 'aws-sdk-sqs',
237
+ SQSSpy.new
238
+ )
239
+ end
240
+ end
@@ -65,6 +65,10 @@ module ElasticAPM
65
65
  base[:destination] = build_destination(context.destination)
66
66
  end
67
67
 
68
+ if context.message
69
+ base[:message] = build_message(context.message)
70
+ end
71
+
68
72
  base
69
73
  end
70
74
 
@@ -89,14 +93,32 @@ module ElasticAPM
89
93
  end
90
94
 
91
95
  def build_destination(destination)
96
+ base =
97
+ {
98
+ service: {
99
+ name: keyword_field(destination.name),
100
+ resource: keyword_field(destination.resource),
101
+ type: keyword_field(destination.type)
102
+ },
103
+ address: keyword_field(destination.address),
104
+ port: destination.port
105
+ }
106
+
107
+ if cloud = destination.cloud
108
+ base[:cloud] = { region: cloud.region }
109
+ end
110
+
111
+ base
112
+ end
113
+
114
+ def build_message(message)
92
115
  {
93
- service: {
94
- name: keyword_field(destination.name),
95
- resource: keyword_field(destination.resource),
96
- type: keyword_field(destination.type)
116
+ queue: {
117
+ name: keyword_field(message.queue_name)
97
118
  },
98
- address: keyword_field(destination.address),
99
- port: destination.port
119
+ age: {
120
+ ms: message.age_ms.to_i
121
+ }
100
122
  }
101
123
  end
102
124
  end
@@ -18,5 +18,5 @@
18
18
  # frozen_string_literal: true
19
19
 
20
20
  module ElasticAPM
21
- VERSION = '3.14.0'
21
+ VERSION = '3.15.0'
22
22
  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: 3.14.0
4
+ version: 3.15.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: 2021-03-17 00:00:00.000000000 Z
11
+ date: 2021-03-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -187,6 +187,7 @@ files:
187
187
  - lib/elastic_apm/span/context/db.rb
188
188
  - lib/elastic_apm/span/context/destination.rb
189
189
  - lib/elastic_apm/span/context/http.rb
190
+ - lib/elastic_apm/span/context/message.rb
190
191
  - lib/elastic_apm/span_helpers.rb
191
192
  - lib/elastic_apm/spies.rb
192
193
  - lib/elastic_apm/spies/action_dispatch.rb
@@ -201,11 +202,14 @@ files:
201
202
  - lib/elastic_apm/spies/rake.rb
202
203
  - lib/elastic_apm/spies/redis.rb
203
204
  - lib/elastic_apm/spies/resque.rb
205
+ - lib/elastic_apm/spies/s3.rb
204
206
  - lib/elastic_apm/spies/sequel.rb
205
207
  - lib/elastic_apm/spies/shoryuken.rb
206
208
  - lib/elastic_apm/spies/sidekiq.rb
207
209
  - lib/elastic_apm/spies/sinatra.rb
208
210
  - lib/elastic_apm/spies/sneakers.rb
211
+ - lib/elastic_apm/spies/sns.rb
212
+ - lib/elastic_apm/spies/sqs.rb
209
213
  - lib/elastic_apm/spies/sucker_punch.rb
210
214
  - lib/elastic_apm/spies/tilt.rb
211
215
  - lib/elastic_apm/sql.rb