elastic-apm 3.14.0 → 3.15.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: 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