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 +4 -4
- data/.rubocop.yml +1 -1
- data/CHANGELOG.asciidoc +18 -1
- data/Gemfile +2 -0
- data/lib/elastic_apm/config.rb +3 -0
- data/lib/elastic_apm/naively_hashable.rb +1 -0
- data/lib/elastic_apm/normalizers/rails/active_record.rb +9 -3
- data/lib/elastic_apm/span/context.rb +10 -2
- data/lib/elastic_apm/span/context/destination.rb +15 -2
- data/lib/elastic_apm/span/context/message.rb +40 -0
- data/lib/elastic_apm/spies/dynamo_db.rb +37 -4
- data/lib/elastic_apm/spies/s3.rb +127 -0
- data/lib/elastic_apm/spies/sns.rb +131 -0
- data/lib/elastic_apm/spies/sqs.rb +240 -0
- data/lib/elastic_apm/transport/serializers/span_serializer.rb +28 -6
- data/lib/elastic_apm/version.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ee845be05bcd6dbd56efb3f21372b0abfede8c37b4c7f9580b82ea6a24238fc5
|
4
|
+
data.tar.gz: 46365fa74214843bc882163c917b429beba61d35f65032f45321ce0e128ecf38
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 821287e4a80cdf707c6bcddb50ff1cf9592e5410c99e09b69dbebbcc4915a9aae968f8bc1704454ad06a964f173e49b8534a5923d059ef4a70417b74b2b285e8
|
7
|
+
data.tar.gz: 5344fb235b378cff3610c0e49771737ba4c906343025110eb38e277f0cb27a6419e76834baf275ebfb2d59ea8e4b94298b65ebe757dcd7fd9cd0d63727a4fe77
|
data/.rubocop.yml
CHANGED
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
|
-
|
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
|
data/lib/elastic_apm/config.rb
CHANGED
@@ -57,12 +57,16 @@ module ElasticAPM
|
|
57
57
|
private
|
58
58
|
|
59
59
|
def subtype_for(payload)
|
60
|
-
|
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
|
-
|
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" &&
|
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
|
-
|
45
|
-
subtype:
|
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
|
-
|
94
|
-
name: keyword_field(
|
95
|
-
resource: keyword_field(destination.resource),
|
96
|
-
type: keyword_field(destination.type)
|
116
|
+
queue: {
|
117
|
+
name: keyword_field(message.queue_name)
|
97
118
|
},
|
98
|
-
|
99
|
-
|
119
|
+
age: {
|
120
|
+
ms: message.age_ms.to_i
|
121
|
+
}
|
100
122
|
}
|
101
123
|
end
|
102
124
|
end
|
data/lib/elastic_apm/version.rb
CHANGED
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.
|
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-
|
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
|