Skip to content

JDBC telemetry customization via builder patterΒ #8802

@lightbody

Description

@lightbody

The current JDBC instrumentation hardcodes the Instrumenter creation logic, which I understand to be a bit of an anti-pattern compared to things like GrpcTelemetryBuilder. So my feature request is to introduce a similar builder that lets me customize naming/attributes for the JDBC spans that get emitted. Here is a rough first draft of what it might look like:

package io.opentelemetry.instrumentation.jdbc.datasource;

import com.google.errorprone.annotations.CanIgnoreReturnValue;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.code.CodeAttributesExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.code.CodeSpanNameExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientSpanNameExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.db.SqlClientAttributesExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesExtractor;
import io.opentelemetry.instrumentation.api.internal.ConfigPropertiesUtil;
import io.opentelemetry.instrumentation.jdbc.internal.DataSourceCodeAttributesGetter;
import io.opentelemetry.instrumentation.jdbc.internal.DbRequest;
import io.opentelemetry.instrumentation.jdbc.internal.JdbcAttributesGetter;
import io.opentelemetry.instrumentation.jdbc.internal.JdbcNetAttributesGetter;

import javax.annotation.Nullable;
import javax.sql.DataSource;
import java.util.function.Function;

public class OpenTelemetryDataSourceBuilder {
    private static final String INSTRUMENTATION_NAME = "io.opentelemetry.jdbc";

    private final DataSource delegate;
    private final OpenTelemetry openTelemetry;

    private @Nullable Function<SpanNameExtractor<DataSource>, ? extends SpanNameExtractor<? super DataSource>> dataSourceNameExtractorTransformer;
    private @Nullable Function<SpanNameExtractor<DbRequest>, ? extends SpanNameExtractor<? super DbRequest>> requestNameExtractorTransformer;


    OpenTelemetryDataSourceBuilder(DataSource delegate, OpenTelemetry openTelemetry) {
        this.delegate = delegate;
        this.openTelemetry = openTelemetry;
    }

    @CanIgnoreReturnValue
    public OpenTelemetryDataSourceBuilder setDataSourceNameExtractor(Function<SpanNameExtractor<DataSource>, ? extends SpanNameExtractor<? super DataSource>> dataSourceNameExtractor) {
        this.dataSourceNameExtractorTransformer = dataSourceNameExtractor;
        return this;
    }

    @CanIgnoreReturnValue
    public OpenTelemetryDataSourceBuilder setRequestNameExtractor(Function<SpanNameExtractor<DbRequest>, ? extends SpanNameExtractor<? super DbRequest>> requestNameExtractor) {
        this.requestNameExtractorTransformer = requestNameExtractor;
        return this;
    }

    public OpenTelemetryDataSource build() {

        // ~~ datasource ~~

        final DataSourceCodeAttributesGetter codeAttributesGetter = new DataSourceCodeAttributesGetter();
        final SpanNameExtractor<? super DataSource> dataSourceNameExtractor = computeDataSourceNameExtractor(codeAttributesGetter);
        final Instrumenter<DataSource, Void> dataSourceInstrumenter = Instrumenter.<DataSource, Void>builder(openTelemetry, INSTRUMENTATION_NAME, dataSourceNameExtractor)
                .addAttributesExtractor(CodeAttributesExtractor.create(codeAttributesGetter))
                .buildInstrumenter();

        // ~~ statement ~~

        final JdbcAttributesGetter dbAttributesGetter = new JdbcAttributesGetter();
        final JdbcNetAttributesGetter netAttributesGetter = new JdbcNetAttributesGetter();
        final SpanNameExtractor<? super DbRequest> requestNameExtractor = requestNameExtractor(dbAttributesGetter);

        final Instrumenter<DbRequest, Void> statementInstrumenter = Instrumenter.<DbRequest, Void>builder(openTelemetry, INSTRUMENTATION_NAME, requestNameExtractor)
                .addAttributesExtractor(
                        SqlClientAttributesExtractor.builder(dbAttributesGetter)
                                .setStatementSanitizationEnabled(
                                        ConfigPropertiesUtil.getBoolean(
                                                "otel.instrumentation.common.db-statement-sanitizer.enabled", true))
                                .build())
                .addAttributesExtractor(NetClientAttributesExtractor.create(netAttributesGetter))
                .buildInstrumenter(SpanKindExtractor.alwaysClient());

        // ~~ return it ~~

        return new OpenTelemetryDataSource(delegate, dataSourceInstrumenter, statementInstrumenter);
    }

    private SpanNameExtractor<? super DataSource> computeDataSourceNameExtractor(DataSourceCodeAttributesGetter codeAttributesGetter) {
        final SpanNameExtractor<DataSource> original = CodeSpanNameExtractor.create(codeAttributesGetter);
        if (dataSourceNameExtractorTransformer != null) {
            return dataSourceNameExtractorTransformer.apply(original);
        }

        return original;
    }

    private SpanNameExtractor<? super DbRequest> requestNameExtractor(JdbcAttributesGetter dbAttributesGetter) {
        final SpanNameExtractor<DbRequest> original = DbClientSpanNameExtractor.create(dbAttributesGetter);
        if (requestNameExtractorTransformer != null) {
            return requestNameExtractorTransformer.apply(original);
        }

        return original;
    }
}
public class OpenTelemetryDataSource implements DataSource, AutoCloseable {

  public static OpenTelemetryDataSourceBuilder builder(DataSource delegate, OpenTelemetry openTelemetry) {
    return new OpenTelemetryDataSourceBuilder(delegate, openTelemetry);
  }
  // continued...

Metadata

Metadata

Assignees

No one assigned

    Labels

    contribution welcomeRequest makes sense, maintainers probably won't have time, contribution would be welcomeenhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions