Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Integration with V3 telemetry provider #1186

Merged
57 changes: 13 additions & 44 deletions instrumentation/aws_sdk/Appraisals
Original file line number Diff line number Diff line change
Expand Up @@ -4,58 +4,27 @@
#
# SPDX-License-Identifier: Apache-2.0

appraise 'aws-sdk-3.1' do
gem 'aws-sdk', '~> 3.1'
appraise 'aws-sdk-3' do
gem 'aws-sdk-core', '~> 3'
gem 'aws-sdk-lambda', '~> 1'
gem 'aws-sdk-dynamodb', '~> 1'
gem 'aws-sdk-sns', '~> 1'
gem 'aws-sdk-sqs', '~> 1'
end
jterapin marked this conversation as resolved.
Show resolved Hide resolved

appraise 'aws-sdk-3.0' do
gem 'aws-sdk', '~> 3.0'
# pre-Observability support in V3 SDK
appraise 'aws-sdk-3.2' do
jterapin marked this conversation as resolved.
Show resolved Hide resolved
gem 'aws-sdk-core', '~> 3.202'
gem 'aws-sdk-lambda', '~> 1.127'
gem 'aws-sdk-dynamodb', '~> 1.118'
gem 'aws-sdk-sns', '~> 1.82'
gem 'aws-sdk-sqs', '~> 1.80'
end

appraise 'aws-sdk-2.11' do
gem 'aws-sdk', '~> 2.11'
end

appraise 'aws-sdk-2.10' do
gem 'aws-sdk', '~> 2.10'
end

appraise 'aws-sdk-2.9' do
gem 'aws-sdk', '~> 2.9'
end

appraise 'aws-sdk-2.8' do
gem 'aws-sdk', '~> 2.8'
end

appraise 'aws-sdk-2.7' do
gem 'aws-sdk', '~> 2.7'
end

appraise 'aws-sdk-2.6' do
gem 'aws-sdk', '~> 2.6'
end

appraise 'aws-sdk-2.5' do
gem 'aws-sdk', '~> 2.5'
end

appraise 'aws-sdk-2.4' do
gem 'aws-sdk', '~> 2.4'
end

appraise 'aws-sdk-2.3' do
gem 'aws-sdk', '~> 2.3'
end

appraise 'aws-sdk-2.2' do
gem 'aws-sdk', '~> 2.2'
end

appraise 'aws-sdk-2.1' do
gem 'aws-sdk', '~> 2.1'
end

appraise 'aws-sdk-2.0' do
gem 'aws-sdk', '~> 2.0'
end
25 changes: 25 additions & 0 deletions instrumentation/aws_sdk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,31 @@ OpenTelemetry::SDK.configure do |c|
c.use_all
end
```
### Configuration options
jterapin marked this conversation as resolved.
Show resolved Hide resolved
This instrumentation offers the following configuration options:
* `:inject_messaging_context` (default: `false`): When set to `true`, adds context key/value
to Message Attributes for SQS/SNS messages.
* `suppress_internal_instrumentation` (default: `false`): When set to `true`, any spans with
span kind of `internal` are suppressed from traces.

## Integration with SDK V3's Telemetry support
AWS SDK for Ruby V3 added support for Observability which includes a configuration,
`telemetry_provider` and an OpenTelemetry-based telemetry provider. Only applies to
AWS service gems released after 2024-09-03.

Using the OTel telemetry provider will give you insights about specific handlers
jterapin marked this conversation as resolved.
Show resolved Hide resolved
during the SDK request/response lifecycle.

```ruby
# configures the OpenTelemetry SDK with instrumentation defaults
OpenTelemetry::SDK.configure do |c|
c.use 'OpenTelemetry::Instrumentation::AwsSdk'
end

# create otel provider and pass to client config
otel_provider = Aws::Telemetry::OTelProvider.new
client = Aws::S3::Client.new(telemetry_provider: otel_provider)
```

## Example

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class Instrumentation < OpenTelemetry::Instrumentation::Base

install do |_config|
require_dependencies
patch if telemetry_plugin?
add_plugins(Seahorse::Client::Base, *loaded_service_clients)
end

Expand Down Expand Up @@ -43,10 +44,28 @@ def require_dependencies
require_relative 'handler'
require_relative 'message_attributes'
require_relative 'messaging_helper'
require_relative 'patches/telemetry'
end

def add_plugins(*targets)
targets.each { |klass| klass.add_plugin(AwsSdk::Plugin) }
targets.each do |klass|
next if supports_telemetry_plugin?(klass)

klass.add_plugin(AwsSdk::Plugin)
end
end

def supports_telemetry_plugin?(klass)
telemetry_plugin? &&
klass.plugins.include?(Aws::Plugins::Telemetry)
end

def telemetry_plugin?
::Aws.const_defined?('Plugins::Telemetry')
jterapin marked this conversation as resolved.
Show resolved Hide resolved
end

def patch
jterapin marked this conversation as resolved.
Show resolved Hide resolved
::Aws::Plugins::Telemetry::Handler.prepend(Patches::Handler)
end

def loaded_service_clients
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,17 @@ def queue_name(context)
'unknown'
end

def span_name(context, client_method)
case client_method
when SQS_SEND_MESSAGE, SQS_SEND_MESSAGE_BATCH, SNS_PUBLISH
"#{client_method}.#{queue_name(context)}.Publish"
when SQS_RECEIVE_MESSAGE
"#{client_method}.#{queue_name(context)}.Receive"
else
client_method
end
end

def legacy_span_name(context, client_method)
case client_method
when SQS_SEND_MESSAGE, SQS_SEND_MESSAGE_BATCH, SNS_PUBLISH
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# frozen_string_literal: true

# Copyright The OpenTelemetry Authors
#
# SPDX-License-Identifier: Apache-2.0

module OpenTelemetry
module Instrumentation
module AwsSdk
module Patches
# Patch for Telemetry Plugin Handler in V3 SDK
module Handler
def call(context)
span_wrapper(context) { @handler.call(context) }
end

private

def span_wrapper(context, &block)
service_id = service_id(context)
client_method = client_method(service_id, context)
context.tracer.in_span(
span_name(context, client_method, service_id),
attributes: attributes(context, client_method, service_id),
kind: span_kind(client_method, service_id)
) do |span|
if instrumentation_config[:inject_messaging_context] &&
%w[SQS SNS].include?(service_id)
MessagingHelper.inject_context(context, client_method)
end

if instrumentation_config[:suppress_internal_instrumentation]
OpenTelemetry::Common::Utilities.untraced { super }
else
yield span
end
end
end

def instrumentation_config
AwsSdk::Instrumentation.instance.config
end

def service_id(context)
context.config.api.metadata['serviceId'] ||
context.config.api.metadata['serviceAbbreviation'] ||
context.config.api.metadata['serviceFullName']
end

def client_method(service_id, context)
"#{service_id}.#{context.operation.name}".delete(' ')
end

def attributes(context, client_method, service_id)
jterapin marked this conversation as resolved.
Show resolved Hide resolved
{
'aws.region' => context.config.region,
OpenTelemetry::SemanticConventions::Trace::RPC_SYSTEM => 'aws-api',
OpenTelemetry::SemanticConventions::Trace::RPC_SERVICE => service_id,
OpenTelemetry::SemanticConventions::Trace::RPC_METHOD => context.operation.name,
OpenTelemetry::SemanticConventions::Trace::CODE_FUNCTION => context.operation_name.to_s,
OpenTelemetry::SemanticConventions::Trace::CODE_NAMESPACE => 'Aws::Plugins::Telemetry'
}.tap do |attrs|
attrs[SemanticConventions::Trace::DB_SYSTEM] = 'dynamodb' if service_id == 'DynamoDB'
jterapin marked this conversation as resolved.
Show resolved Hide resolved
MessagingHelper.apply_span_attributes(context, attrs, client_method, service_id) if %w[SQS SNS].include?(service_id)
end
end

def span_name(context, client_method, service_id)
case service_id
when 'SQS', 'SNS'
MessagingHelper.span_name(context, client_method)
else
client_method
end
end

def span_kind(client_method, service_id)
case service_id
when 'SQS', 'SNS'
MessagingHelper.span_kind(client_method)
else
OpenTelemetry::Trace::SpanKind::CLIENT
end
end
end
end
end
end
end
Loading
Loading