open-telemetry / opentelemetry-dotnet-contrib

This repository contains set of components extending functionality of the OpenTelemetry .NET SDK. Instrumentation libraries, exporters, and other components can find their home here.
https://opentelemetry.io
Apache License 2.0
477 stars 283 forks source link

[feature request] Add something similar to AWSLambdaWrapper for SQS Consumers that are not Lambda #2244

Open xactlance opened 1 month ago

xactlance commented 1 month ago

Component

None

Is your feature request related to a problem?

I had to get custom to implement extracting traceparent + tracestate from my SQS consuming BackgroundService. A lot of it's inspiration came from AWSLambdaWrapper.Trace. I would like the same thing as AWSLambdaWrapper.Trace for non-lambda SQS consumers.

What is the expected behavior?

There is a Trace extraction and propagation wrapper/helper for non-Lambda SQS Consumers (BackgroundService, CLI tool, whatever).

Which alternative solutions or features have you considered?

public static void Main(string[] args)
{
    Host
        .CreateDefaultBuilder(args)
        .ConfigureServices((context, services) =>
        {
            var activitySource = new ActivitySource(Assembly.GetExecutingAssembly().FullName!);
            services.AddSingleton(activitySource);
            services.AddAWSService<IAmazonSimpleNotificationService>();
            services.AddAWSService<IAmazonSQS>();
            services
                .AddOpenTelemetry()
                .WithTracing(tracerBuilder =>
                {
                    tracerBuilder
                        .AddSource(activitySource.Name)
                        .AddAWSInstrumentation();
                });
            services.AddSingleton<IHostedService, Service>();
        })
        .Build()
        .Run();
}

public class Service : BackgroundService
{
    private readonly ActivitySource _activitySource;
    private readonly IAmazonSQS _sqsClient;
    private readonly IAmazonSimpleNotificationService _snsClient;

    public Service(ActivitySource activitySource, IAmazonSQS sqsClient, IAmazonSimpleNotificationService snsClient)
    {
        _activitySource = activitySource;
        _sqsClient = sqsClient;
        _snsClient = snsClient;
    }

    protected async override Task ExecuteAsync(CancellationToken cancellationToken)
    {
        var receiveMessageResponse = await _sqsClient.ReceiveMessageAsync(
            new ReceiveMessageRequest 
            {
                QueueUrl = "queue-url",
                MessageAttributeNames = ["traceparent", "tracestate"],
            },
            cancellationToken);

        if (!receiveMessageResponse.Messages.Any())
        {
            return;
        }

        foreach (var message in receiveMessageResponse.Messages)
        {
            // Extract incoming trace information from the SQSMessage, if there is any.
            var parentContext = TraceContext.Extract(message);
            // Start an Activity, which is .NETs way of starting Traces. If there was a trace extracted from the
            // incoming SQSMessage then that trace will be continued. If not, then a new trace will be started.
            using (_activitySource.StartActivity("ExecuteAsync", ActivityKind.Server, parentContext.ActivityContext))
            {
                // This call will now automatically include traceparent and tracestate in MessageAttributes
                // thanks to the "AddAWSInstrumentation(...)".
                await _snsClient.PublishAsync(...);
            }
        }
    }
}

Additional context

No response

Kielek commented 1 month ago

@ppittle, @birojnayak, @srprash, please check