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

Allow for simpler creation of start-only and end-only SpanProcessors. #5923

Merged
merged 6 commits into from
Dec 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,18 @@

package io.opentelemetry.sdk.autoconfigure.provider;

import io.opentelemetry.context.Context;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.trace.ReadWriteSpan;
import io.opentelemetry.sdk.trace.ReadableSpan;
import io.opentelemetry.sdk.extension.incubator.trace.OnStartSpanProcessor;
import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder;
import io.opentelemetry.sdk.trace.SpanProcessor;

@SuppressWarnings("deprecation") // Support testing of SdkTracerProviderConfigurer
public class TestTracerProviderConfigurer
implements io.opentelemetry.sdk.autoconfigure.spi.traces.SdkTracerProviderConfigurer {
@Override
public void configure(SdkTracerProviderBuilder tracerProvider, ConfigProperties config) {
tracerProvider.addSpanProcessor(
new SpanProcessor() {
@Override
public void onStart(Context parentContext, ReadWriteSpan span) {
span.setAttribute("configured", config.getBoolean("otel.test.configured"));
}

@Override
public boolean isStartRequired() {
return true;
}

@Override
public void onEnd(ReadableSpan span) {}

@Override
public boolean isEndRequired() {
return false;
}
});
OnStartSpanProcessor.create(
(ctx, span) ->
span.setAttribute("configured", config.getBoolean("otel.test.configured"))));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.sdk.extension.incubator.trace;

import io.opentelemetry.context.Context;
import io.opentelemetry.sdk.trace.ReadWriteSpan;
import io.opentelemetry.sdk.trace.ReadableSpan;
import io.opentelemetry.sdk.trace.SpanProcessor;

/** A SpanProcessor implementation that is only capable of processing spans when they end. */
public final class OnEndSpanProcessor implements SpanProcessor {
private final OnEnd onEnd;

private OnEndSpanProcessor(OnEnd onEnd) {
this.onEnd = onEnd;
}

static SpanProcessor create(OnEnd onEnd) {
return new OnEndSpanProcessor(onEnd);
}

@Override
public void onEnd(ReadableSpan span) {
onEnd.apply(span);
}

@Override
public boolean isEndRequired() {
return true;
}

@Override
public void onStart(Context parentContext, ReadWriteSpan span) {
// nop
}

Check warning on line 39 in sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/trace/OnEndSpanProcessor.java

View check run for this annotation

Codecov / codecov/patch

sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/trace/OnEndSpanProcessor.java#L38-L39

Added lines #L38 - L39 were not covered by tests
@Override
public boolean isStartRequired() {
return false;
}

@FunctionalInterface
public interface OnEnd {
void apply(ReadableSpan span);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

apply or onEnd?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used apply for both OnStart and OnEnd. I read it as a functional interface, so an implementation or lambda instance of OnEnd is the function and the function gets applied. At least that's how my brain thinks of it.

I think the name isn't hugely important here, but OnEnd.onEnd() seems a little silly. I think one could also make the case that it's a consumer, so it should be called accept(), but then OnStart has two args.

I guess I think apply is the most straightforward.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this too silly?

@FunctionalInterface
public interface OnEndSpanProcessor extends SpanProcessor {

  // this is the kind of silly part...
  static SpanProcessor create(OnEndSpanProcessor onEnd) {
    return onEnd;
  }

  @Override
  default boolean isEndRequired() {
    return true;
  }

  @Override
  default void onStart(Context parentContext, ReadWriteSpan span) {
    // nop
  }

  @Override
  default boolean isStartRequired() {
    return false;
  }
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's what I suggested in this comment @trask.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thx for the link 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this too silly?

It's not, but it does allow people to still do silly things with it. :)

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.sdk.extension.incubator.trace;

import io.opentelemetry.context.Context;
import io.opentelemetry.sdk.trace.ReadWriteSpan;
import io.opentelemetry.sdk.trace.ReadableSpan;
import io.opentelemetry.sdk.trace.SpanProcessor;

/** A SpanProcessor that only handles onStart(). */
public final class OnStartSpanProcessor implements SpanProcessor {

private final OnStart onStart;

private OnStartSpanProcessor(OnStart onStart) {
this.onStart = onStart;
}

public static SpanProcessor create(OnStart onStart) {
return new OnStartSpanProcessor(onStart);
}

@Override
public void onStart(Context parentContext, ReadWriteSpan span) {
onStart.apply(parentContext, span);
}

@Override
public boolean isStartRequired() {
return true;
}

@Override
public void onEnd(ReadableSpan span) {
// nop
}

@Override
public boolean isEndRequired() {
return false;
}

@FunctionalInterface
public interface OnStart {
void apply(Context context, ReadWriteSpan span);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.sdk.extension.incubator.trace;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;

import io.opentelemetry.sdk.trace.ReadWriteSpan;
import io.opentelemetry.sdk.trace.ReadableSpan;
import io.opentelemetry.sdk.trace.SpanProcessor;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.jupiter.api.Test;

class OnEndSpanProcessorTest {

@Test
void endOnly() {
AtomicReference<ReadableSpan> seenSpan = new AtomicReference<>();
ReadWriteSpan inputSpan = mock(ReadWriteSpan.class);

SpanProcessor processor = OnEndSpanProcessor.create(seenSpan::set);

assertThat(processor.isStartRequired()).isFalse();
assertThat(processor.isEndRequired()).isTrue();
processor.onEnd(inputSpan);
assertThat(seenSpan.get()).isSameAs(inputSpan);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.sdk.extension.incubator.trace;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;

import io.opentelemetry.context.Context;
import io.opentelemetry.sdk.trace.ReadWriteSpan;
import io.opentelemetry.sdk.trace.SpanProcessor;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.jupiter.api.Test;

class OnStartSpanProcessorTest {

@Test
void startOnly() {
AtomicReference<Context> seenContext = new AtomicReference<>();
AtomicReference<ReadWriteSpan> seenSpan = new AtomicReference<>();
Context context = mock(Context.class);
ReadWriteSpan inputSpan = mock(ReadWriteSpan.class);

SpanProcessor processor =
OnStartSpanProcessor.create(
(ctx, span) -> {
seenContext.set(ctx);
seenSpan.set(span);
});

assertThat(processor.isStartRequired()).isTrue();
assertThat(processor.isEndRequired()).isFalse();
processor.onStart(context, inputSpan);
assertThat(seenContext.get()).isSameAs(context);
assertThat(seenSpan.get()).isSameAs(inputSpan);
}
}