open-telemetry / open-telemetry/opentelemetry-java

Allow overwriting/modyfing span resource in SpanProcessor

Open
#5,395 6 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

blocked:spec Feature Request
Dominant language
Java
Stars
2.5k
Forks
1k
Avg merge
3d 17h
Merged PRs (30d)
58

Description

Is your feature request related to a problem? Please describe.

I'm using OTEL with DataDog (DD), and it would greatly help visibility if I could overwrite the service name for some operations (spans).

DD doesn't properly understand the semantics of OTEL, and the problem is extra visible when some services are instrumented using OTEL, and some are instrumented using the native DD agent. DD creates a separate "service" for database calls, HTTP calls, etc - basically, everything that "leaves" the service and that's super useful, because then the time spent in DB or HTTP calls really pops out and becomes super visible.

Describe the solution you'd like

Basically I'd like to be able to modify the resource of a span. Right now the resource field is final and ReadWriteSpan cannot modify it.

Describe alternatives you've considered

Currently, I'm experimenting with hacking it using reflection, but I would like to avoid having to use that in the PROD... but as a prototype, it works really well...

OverrideServiceNameSpanProcessor
public class OverrideServiceNameSpanProcessor implements SpanProcessor
{

    private static final Class<?> SDK_SPAN_CLASS;
    private static final Field SDK_SPAN_RESOURCE_FIELD;

    static {
        try {
            SDK_SPAN_CLASS = Class.forName("io.opentelemetry.sdk.trace.SdkSpan");

            SDK_SPAN_RESOURCE_FIELD = SDK_SPAN_CLASS.getDeclaredField("resource");
            SDK_SPAN_RESOURCE_FIELD.setAccessible(true);

        } catch (ClassNotFoundException | NoSuchFieldException e) {
            throw new IllegalStateException(e);
        }
    }

    private static final Map<String, String> SCOPE_TO_SERVICE_NAME_SUFFIX = Map.ofEntries(
        Map.entry("io.opentelemetry.jdbc", "jdbc"),
        Map.entry("io.opentelemetry.apache-httpclient-5.0", "httpclient"),
        Map.entry("io.opentelemetry.http-url-connection", "httpclient"),
        Map.entry("io.opentelemetry.aws-sdk-1.11", "aws")
    );

    @Override
    public void onStart(final Context parentContext, final ReadWriteSpan span)
    {
        if (!SDK_SPAN_CLASS.isInstance(span)) {
            return; // noop
        }

        InstrumentationScopeInfo instrumentationInfo = span.getInstrumentationScopeInfo();
        @Nullable String resourceServiceNameSuffix = SCOPE_TO_SERVICE_NAME_SUFFIX.get(instrumentationInfo.getName());
        if (resourceServiceNameSuffix == null) {
            return; // noop
        }

        try {
            var resource = (Resource) SDK_SPAN_RESOURCE_FIELD.get(span);

            Resource updatedResource = resource.toBuilder()
                .put(ResourceAttributes.SERVICE_NAME, "%s-%s".formatted(
                    resource.getAttribute(ResourceAttributes.SERVICE_NAME),
                    resourceServiceNameSuffix
                ))
                .build();

            SDK_SPAN_RESOURCE_FIELD.set(span, updatedResource);

        } catch (IllegalAccessException e) {
            throw new IllegalStateException(e);
        }
    }

    // the rest is not important

}

Additional context

You have to zoom in a little, but then you can see that the e.g. redis, pdo, mongo, etc. are shown as separate services - these spans are obviously part of the app that is calling them, but the native DD agent renames the single span's service name.

It makes a lot of sense if you think about it for a bit - my database is obviously not instrumented with OTEL (maybe in the future?), so I don't have any spans from it - but I have the spans in my app that are calling the database and that is as close as I'll get in the near future to having the database calls rendered as a separate service.

image

The example image is not mine it's from some article - but it illustrates it nicely.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the ReadWriteSpan and SdkSpan APIs, especially the resource field, and review how SpanProcessor receives spans and how Resource is represented. The issue does not name a test or file beyond these entry points; done means defining and validating a supported way for processors to modify a span resource without reflection.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
observability-sre
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.