Skip to content

Commit dafe8c5

Browse files
authored
Observability Support (#3092)
1 parent a7bfec2 commit dafe8c5

File tree

30 files changed

+1462
-25
lines changed

30 files changed

+1462
-25
lines changed

Gemfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ group :test do
6565
gem 'addressable'
6666
gem 'cucumber'
6767
gem 'webmock'
68-
6968
gem 'multipart-post'
7069
gem 'rspec'
70+
gem 'opentelemetry-sdk'
7171
end

build_tools/aws-sdk-code-generator/lib/aws-sdk-code-generator/plugin_list.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ def default_plugins
6363
'Aws::Plugins::ChecksumAlgorithm' => "#{core_plugins}/checksum_algorithm.rb",
6464
'Aws::Plugins::RequestCompression' => "#{core_plugins}/request_compression.rb",
6565
'Aws::Plugins::DefaultsMode' => "#{core_plugins}/defaults_mode.rb",
66-
'Aws::Plugins::RecursionDetection' => "#{core_plugins}/recursion_detection.rb"
66+
'Aws::Plugins::RecursionDetection' => "#{core_plugins}/recursion_detection.rb",
67+
'Aws::Plugins::Telemetry' => "#{core_plugins}/telemetry.rb"
6768
}
6869
end
6970

build_tools/aws-sdk-code-generator/templates/async_client_class.mustache

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,18 @@ module {{module_name}}
8383
# @api private
8484
def build_request(operation_name, params = {})
8585
handlers = @handlers.for(operation_name)
86+
tracer = config.telemetry_provider.tracer_provider.tracer(
87+
Aws::Telemetry.module_to_tracer_name('{{module_name}}')
88+
)
8689
context = Seahorse::Client::RequestContext.new(
8790
operation_name: operation_name,
8891
operation: config.api.operation(operation_name),
8992
client: self,
9093
params: params,
9194
http_response: Seahorse::Client::Http::AsyncResponse.new,
92-
config: config)
95+
config: config,
96+
tracer: tracer
97+
)
9398
context[:gem_name] = '{{gem_name}}'
9499
context[:gem_version] = '{{gem_version}}'
95100
Seahorse::Client::Request.new(handlers, context)

build_tools/aws-sdk-code-generator/templates/client_class.mustache

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,13 +99,18 @@ module {{module_name}}
9999
end
100100
end
101101
{{/authorizer?}}
102+
tracer = config.telemetry_provider.tracer_provider.tracer(
103+
Aws::Telemetry.module_to_tracer_name('{{module_name}}')
104+
)
102105
context = Seahorse::Client::RequestContext.new(
103106
operation_name: operation_name,
104107
operation: config.api.operation(operation_name),{{#authorizer?}}
105108
authorizer: authorizer,{{/authorizer?}}
106109
client: self,
107110
params: params,
108-
config: config)
111+
config: config,
112+
tracer: tracer
113+
)
109114
context[:gem_name] = '{{gem_name}}'
110115
context[:gem_version] = '{{gem_version}}'
111116
Seahorse::Client::Request.new(handlers, context)

build_tools/customizations.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,13 +107,16 @@ def dynamodb_example_deep_transform(subsegment, keys)
107107
end
108108

109109
api('ImportExport') do |api|
110+
api['metadata']['serviceId'] ||= 'importexport'
111+
110112
api['operations'].each do |_, operation|
111113
operation['http']['requestUri'] = '/'
112114
end
113115
end
114116

115117
%w(Lambda LambdaPreview).each do |svc_name|
116118
api(svc_name) do |api|
119+
api['metadata']['serviceId'] ||= 'Lambda Preview' if svc_name == 'LambdaPreview'
117120
api['shapes']['Timestamp']['type'] = 'timestamp'
118121
end
119122

@@ -220,6 +223,8 @@ def dynamodb_example_deep_transform(subsegment, keys)
220223
# uses both flattened and locationName. Query protocol is supposed to
221224
# ignore location name (xmlName) when flattened (xmlFlattened) is used.
222225
api('SimpleDB') do |api|
226+
api['metadata']['serviceId'] ||= 'SimpleDB'
227+
223228
api['shapes'].each do |_, shape|
224229
next unless shape['type'] == 'structure'
225230

build_tools/services.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ class ServiceEnumerator
1010
MANIFEST_PATH = File.expand_path('../../services.json', __FILE__)
1111

1212
# Minimum `aws-sdk-core` version for new gem builds
13-
MINIMUM_CORE_VERSION = "3.201.0"
13+
MINIMUM_CORE_VERSION = "3.203.0"
1414

1515
# Minimum `aws-sdk-core` version for new S3 gem builds
16-
MINIMUM_CORE_VERSION_S3 = "3.201.0"
16+
MINIMUM_CORE_VERSION_S3 = "3.203.0"
1717

1818
EVENTSTREAM_PLUGIN = "Aws::Plugins::EventStreamConfiguration"
1919

gems/aws-sdk-core/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
Unreleased Changes
22
------------------
33

4+
* Feature - Add support for Observability which includes a configuration, `telemetry_provider` and an OpenTelemetry-based telemetry provider.
5+
46
3.202.2 (2024-08-30)
57
------------------
68

gems/aws-sdk-core/lib/aws-sdk-core.rb

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
require_relative 'aws-sdk-core/deprecations'
88

99
# credential providers
10-
1110
require_relative 'aws-sdk-core/credential_provider'
1211
require_relative 'aws-sdk-core/refreshing_credentials'
1312
require_relative 'aws-sdk-core/assume_role_credentials'
@@ -30,7 +29,6 @@
3029
require_relative 'aws-sdk-core/plugins/bearer_authorization'
3130

3231
# client modules
33-
3432
require_relative 'aws-sdk-core/client_stubs'
3533
require_relative 'aws-sdk-core/async_client_stubs'
3634
require_relative 'aws-sdk-core/eager_loader'
@@ -45,24 +43,20 @@
4543
require_relative 'aws-sdk-core/util'
4644

4745
# resource classes
48-
4946
require_relative 'aws-sdk-core/resources/collection'
5047

5148
# logging
52-
5349
require_relative 'aws-sdk-core/log/formatter'
5450
require_relative 'aws-sdk-core/log/param_filter'
5551
require_relative 'aws-sdk-core/log/param_formatter'
5652

5753
# stubbing
58-
5954
require_relative 'aws-sdk-core/stubbing/empty_stub'
6055
require_relative 'aws-sdk-core/stubbing/data_applicator'
6156
require_relative 'aws-sdk-core/stubbing/stub_data'
6257
require_relative 'aws-sdk-core/stubbing/xml_error'
6358

6459
# stubbing protocols
65-
6660
require_relative 'aws-sdk-core/stubbing/protocols/json'
6761
require_relative 'aws-sdk-core/stubbing/protocols/rest'
6862
require_relative 'aws-sdk-core/stubbing/protocols/rest_json'
@@ -73,7 +67,6 @@
7367
require_relative 'aws-sdk-core/stubbing/protocols/api_gateway'
7468

7569
# protocols
76-
7770
require_relative 'aws-sdk-core/error_handler'
7871
require_relative 'aws-sdk-core/rest'
7972
require_relative 'aws-sdk-core/xml'
@@ -82,21 +75,18 @@
8275
require_relative 'aws-sdk-core/rpc_v2'
8376

8477
# event stream
85-
8678
require_relative 'aws-sdk-core/binary'
8779
require_relative 'aws-sdk-core/event_emitter'
8880

8981
# endpoint discovery
90-
9182
require_relative 'aws-sdk-core/endpoint_cache'
9283

93-
# client metrics
94-
84+
# client metrics / telemetry
9585
require_relative 'aws-sdk-core/client_side_monitoring/request_metrics'
9686
require_relative 'aws-sdk-core/client_side_monitoring/publisher'
87+
require_relative 'aws-sdk-core/telemetry'
9788

9889
# utilities
99-
10090
require_relative 'aws-sdk-core/arn'
10191
require_relative 'aws-sdk-core/arn_parser'
10292
require_relative 'aws-sdk-core/ec2_metadata'

gems/aws-sdk-core/lib/aws-sdk-core/plugins/stub_responses.rb

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,14 @@ def after_initialize(client)
4949
class Handler < Seahorse::Client::Handler
5050

5151
def call(context)
52+
span_wrapper(context) do
53+
stub_responses(context)
54+
end
55+
end
56+
57+
private
58+
59+
def stub_responses(context)
5260
stub = context.client.next_stub(context)
5361
resp = Seahorse::Client::Response.new(context: context)
5462
async_mode = context.client.is_a? Seahorse::Client::AsyncBase
@@ -58,8 +66,15 @@ def call(context)
5866
apply_stub(stub, resp, async_mode)
5967
end
6068

61-
async_mode ? Seahorse::Client::AsyncResponse.new(
62-
context: context, stream: context[:input_event_stream_handler].event_emitter.stream, sync_queue: Queue.new) : resp
69+
if async_mode
70+
Seahorse::Client::AsyncResponse.new(
71+
context: context,
72+
stream: context[:input_event_stream_handler].event_emitter.stream,
73+
sync_queue: Queue.new
74+
)
75+
else
76+
resp
77+
end
6378
end
6479

6580
def apply_stub(stub, response, async_mode = false)
@@ -99,6 +114,18 @@ def signal_http(stub, http_resp, async_mode = false)
99114
http_resp.signal_done
100115
end
101116

117+
def span_wrapper(context, &block)
118+
context.tracer.in_span(
119+
'Handler.StubResponses',
120+
attributes: Aws::Telemetry.http_request_attrs(context)
121+
) do |span|
122+
block.call.tap do
123+
span.add_attributes(
124+
Aws::Telemetry.http_response_attrs(context)
125+
)
126+
end
127+
end
128+
end
102129
end
103130
end
104131
end
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# frozen_string_literal: true
2+
3+
module Aws
4+
module Plugins
5+
# @api private
6+
class Telemetry < Seahorse::Client::Plugin
7+
option(
8+
:telemetry_provider,
9+
default: Aws::Telemetry::NoOpTelemetryProvider,
10+
doc_type: Aws::Telemetry::TelemetryProviderBase,
11+
rbs_type: Aws::Telemetry::TelemetryProviderBase,
12+
docstring: <<-DOCS) do |_cfg|
13+
Allows you to provide a telemetry provider, which is used to
14+
emit telemetry data. By default, uses `NoOpTelemetryProvider` which
15+
will not record or emit any telemetry data. The SDK supports the
16+
following telemetry providers:
17+
18+
* OpenTelemetry (OTel) - To use the OTel provider, install and require the
19+
`opentelemetry-sdk` gem and then, pass in an instance of a
20+
`Aws::Telemetry::OTelProvider` for telemetry provider.
21+
DOCS
22+
Aws::Telemetry::NoOpTelemetryProvider.new
23+
end
24+
25+
def after_initialize(client)
26+
validate_telemetry_provider(client.config)
27+
end
28+
29+
def validate_telemetry_provider(config)
30+
unless config.telemetry_provider.is_a?(Aws::Telemetry::TelemetryProviderBase)
31+
raise ArgumentError,
32+
'Must provide a telemetry provider for the '\
33+
'`telemetry_provider` configuration option.'
34+
end
35+
end
36+
37+
class Handler < Seahorse::Client::Handler
38+
def call(context)
39+
span_wrapper(context) { @handler.call(context) }
40+
end
41+
42+
private
43+
44+
def span_wrapper(context, &block)
45+
service_id = service_id(context)
46+
attributes = {
47+
'rpc.system' => 'aws-api',
48+
'rpc.service' => service_id,
49+
'rpc.method' => context.operation.name,
50+
'code.function' => context.operation_name.to_s,
51+
'code.namespace' => 'Aws::Plugins::Telemetry'
52+
}
53+
context.tracer.in_span(
54+
parent_span_name(context, service_id),
55+
attributes: attributes,
56+
kind: Aws::Telemetry::SpanKind::CLIENT,
57+
&block
58+
)
59+
end
60+
61+
def service_id(context)
62+
context.config.api.metadata['serviceId'] ||
63+
context.config.api.metadata['serviceAbbreviation'] ||
64+
context.config.api.metadata['serviceFullName']
65+
end
66+
67+
def parent_span_name(context, service_id)
68+
"#{service_id}.#{context.operation.name}".delete(' ')
69+
end
70+
end
71+
72+
handler(Handler, step: :initialize, priority: 99)
73+
end
74+
end
75+
end

0 commit comments

Comments
 (0)