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

Refactor ExtendedTracer, ExtendedSpanBuilder to reflect incubating API conventions #6497

Merged
merged 1 commit into from
Jun 6, 2024
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
174 changes: 7 additions & 167 deletions api/incubator/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,170 +35,10 @@ Features:

See [ExtendedContextPropagatorsUsageTest](./src/test/java/io/opentelemetry/api/incubator/propagation/ExtendedContextPropagatorsUsageTest.java).

## ExtendedTracer

Utility methods to make it easier to use the OpenTelemetry tracer.

Here are some examples how the utility methods can help reduce boilerplate code.

TODO: translate examples to test to ensure no java compilation issues.

### Tracing a function

Before:

<!-- markdownlint-disable -->
```java
Span span = tracer.spanBuilder("reset_checkout").startSpan();
String transactionId;
try (Scope scope = span.makeCurrent()) {
transactionId = resetCheckout(cartId);
} catch (Throwable e) {
span.setStatus(StatusCode.ERROR);
span.recordException(e);
throw e; // or throw new RuntimeException(e) - depending on your error handling strategy
} finally {
span.end();
}
```
<!-- markdownlint-enable -->

After:

```java
import io.opentelemetry.extension.incubator.trace.ExtendedTracer;

ExtendedTracer extendedTracer = ExtendedTracer.create(tracer);
String transactionId = extendedTracer.spanBuilder("reset_checkout").startAndCall(() -> resetCheckout(cartId));
```

If you want to set attributes on the span, you can use the `startAndCall` method on the span builder:

```java
import io.opentelemetry.extension.incubator.trace.ExtendedTracer;

ExtendedTracer extendedTracer = ExtendedTracer.create(tracer);
String transactionId = extendedTracer.spanBuilder("reset_checkout")
.setAttribute("foo", "bar")
.startAndCall(() -> resetCheckout(cartId));
```

Note:

- Use `startAndRun` instead of `startAndCall` if the function returns `void` (both on the tracer and span builder).
- Exceptions are re-thrown without modification - see [Exception handling](#exception-handling)
for more details.

### Trace context propagation

Before:

```java
Map<String, String> propagationHeaders = new HashMap<>();
openTelemetry
.getPropagators()
.getTextMapPropagator()
.inject(
Context.current(),
propagationHeaders,
(map, key, value) -> {
if (map != null) {
map.put(key, value);
}
});

// add propagationHeaders to request headers and call checkout service
```

<!-- markdownlint-disable -->
```java
// in checkout service: get request headers into a Map<String, String> requestHeaders
Map<String, String> requestHeaders = new HashMap<>();
String cartId = "cartId";

SpanBuilder spanBuilder = tracer.spanBuilder("checkout_cart");

TextMapGetter<Map<String, String>> TEXT_MAP_GETTER =
new TextMapGetter<Map<String, String>>() {
@Override
public Set<String> keys(Map<String, String> carrier) {
return carrier.keySet();
}

@Override
@Nullable
public String get(@Nullable Map<String, String> carrier, String key) {
return carrier == null ? null : carrier.get(key);
}
};

Map<String, String> normalizedTransport =
requestHeaders.entrySet().stream()
.collect(
Collectors.toMap(
entry -> entry.getKey().toLowerCase(Locale.ROOT), Map.Entry::getValue));
Context newContext = openTelemetry
.getPropagators()
.getTextMapPropagator()
.extract(Context.current(), normalizedTransport, TEXT_MAP_GETTER);
String transactionId;
try (Scope ignore = newContext.makeCurrent()) {
Span span = spanBuilder.setSpanKind(SERVER).startSpan();
try (Scope scope = span.makeCurrent()) {
transactionId = processCheckout(cartId);
} catch (Throwable e) {
span.setStatus(StatusCode.ERROR);
span.recordException(e);
throw e; // or throw new RuntimeException(e) - depending on your error handling strategy
} finally {
span.end();
}
}
```
<!-- markdownlint-enable -->

After:

```java
import io.opentelemetry.extension.incubator.propagation.ExtendedContextPropagators;

Map<String, String> propagationHeaders =
ExtendedContextPropagators.getTextMapPropagationContext(openTelemetry.getPropagators());
// add propagationHeaders to request headers and call checkout service
```

```java
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.extension.incubator.trace.ExtendedTracer;

// in checkout service: get request headers into a Map<String, String> requestHeaders
Map<String, String> requestHeaders = new HashMap<>();
String cartId = "cartId";

ExtendedTracer extendedTracer = ExtendedTracer.create(tracer);
String transactionId = extendedTracer.spanBuilder("checkout_cart")
.setSpanKind(SpanKind.SERVER)
.setParentFrom(openTelemetry.getPropagators(), requestHeaders)
.startAndCall(() -> processCheckout(cartId));
```

### Exception handling

`ExtendedTracer` re-throws exceptions without modification. This means you can
catch exceptions around `ExtendedTracer` calls and handle them as you would without `ExtendedTracer`.

When an exception is encountered during an `ExtendedTracer` call, the span is marked as error and
the exception is recorded.

If you want to customize this behaviour, e.g. to only record the exception, because you are
able to recover from the error, you can call the overloaded method of `startAndCall` or
`startAndRun` that takes an exception handler:

```java
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.extension.incubator.trace.ExtendedTracer;

ExtendedTracer extendedTracer = ExtendedTracer.create(tracer);
String transactionId = extendedTracer.spanBuilder("checkout_cart")
.startAndCall(() -> processCheckout(cartId), Span::recordException);
```
## Extended Trace APIs

Features:

* Utility methods to reduce boilerplace using span API, including extracting context, and wrapping runnables / callables with spans

See [ExtendedTraceApiUsageTest](./src/test/java/io/opentelemetry/api/incubator/trace/ExtendedTraceApiUsageTest.java).
Original file line number Diff line number Diff line change
Expand Up @@ -6,106 +6,14 @@
package io.opentelemetry.api.incubator.trace;

import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.incubator.propagation.ExtendedContextPropagators;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanBuilder;
import io.opentelemetry.api.trace.SpanContext;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import io.opentelemetry.context.propagation.ContextPropagators;
import java.time.Instant;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;

public final class ExtendedSpanBuilder implements SpanBuilder {
private final SpanBuilder delegate;

ExtendedSpanBuilder(SpanBuilder delegate) {
this.delegate = delegate;
}

@Override
public ExtendedSpanBuilder setParent(Context context) {
delegate.setParent(context);
return this;
}

@Override
public ExtendedSpanBuilder setNoParent() {
delegate.setNoParent();
return this;
}

@Override
public ExtendedSpanBuilder addLink(SpanContext spanContext) {
delegate.addLink(spanContext);
return this;
}

@Override
public ExtendedSpanBuilder addLink(SpanContext spanContext, Attributes attributes) {
delegate.addLink(spanContext, attributes);
return this;
}

@Override
public ExtendedSpanBuilder setAttribute(String key, String value) {
delegate.setAttribute(key, value);
return this;
}

@Override
public ExtendedSpanBuilder setAttribute(String key, long value) {
delegate.setAttribute(key, value);
return this;
}

@Override
public ExtendedSpanBuilder setAttribute(String key, double value) {
delegate.setAttribute(key, value);
return this;
}

@Override
public ExtendedSpanBuilder setAttribute(String key, boolean value) {
delegate.setAttribute(key, value);
return this;
}

@Override
public <T> ExtendedSpanBuilder setAttribute(AttributeKey<T> key, T value) {
delegate.setAttribute(key, value);
return this;
}

@Override
public ExtendedSpanBuilder setAllAttributes(Attributes attributes) {
delegate.setAllAttributes(attributes);
return this;
}

@Override
public ExtendedSpanBuilder setSpanKind(SpanKind spanKind) {
delegate.setSpanKind(spanKind);
return this;
}

@Override
public ExtendedSpanBuilder setStartTimestamp(long startTimestamp, TimeUnit unit) {
delegate.setStartTimestamp(startTimestamp, unit);
return this;
}

@Override
public ExtendedSpanBuilder setStartTimestamp(Instant startTimestamp) {
delegate.setStartTimestamp(startTimestamp);
return this;
}
/** Extended {@link SpanBuilder} with experimental APIs. */
public interface ExtendedSpanBuilder extends SpanBuilder {

/**
* Extract a span context from the given carrier and set it as parent of the span for {@link
Expand All @@ -124,16 +32,7 @@ public ExtendedSpanBuilder setStartTimestamp(Instant startTimestamp) {
* @param propagators provide the propagators from {@link OpenTelemetry#getPropagators()}
* @param carrier the string map where to extract the span context from
*/
public ExtendedSpanBuilder setParentFrom(
ContextPropagators propagators, Map<String, String> carrier) {
setParent(ExtendedContextPropagators.extractTextMapPropagationContext(carrier, propagators));
return this;
}

@Override
public Span startSpan() {
return delegate.startSpan();
}
ExtendedSpanBuilder setParentFrom(ContextPropagators propagators, Map<String, String> carrier);

/**
* Runs the given {@link SpanCallable} inside of the span created by the given {@link
Expand All @@ -147,9 +46,7 @@ public Span startSpan() {
* @param <E> the type of the exception
* @return the result of the {@link SpanCallable}
*/
public <T, E extends Throwable> T startAndCall(SpanCallable<T, E> spanCallable) throws E {
return startAndCall(spanCallable, ExtendedSpanBuilder::setSpanError);
}
<T, E extends Throwable> T startAndCall(SpanCallable<T, E> spanCallable) throws E;

/**
* Runs the given {@link SpanCallable} inside of the span created by the given {@link
Expand All @@ -165,20 +62,8 @@ public <T, E extends Throwable> T startAndCall(SpanCallable<T, E> spanCallable)
* @param <E> the type of the exception
* @return the result of the {@link SpanCallable}
*/
public <T, E extends Throwable> T startAndCall(
SpanCallable<T, E> spanCallable, BiConsumer<Span, Throwable> handleException) throws E {
Span span = startSpan();

//noinspection unused
try (Scope unused = span.makeCurrent()) {
return spanCallable.callInSpan();
} catch (Throwable e) {
handleException.accept(span, e);
throw e;
} finally {
span.end();
}
}
<T, E extends Throwable> T startAndCall(
SpanCallable<T, E> spanCallable, BiConsumer<Span, Throwable> handleException) throws E;

/**
* Runs the given {@link SpanRunnable} inside of the span created by the given {@link
Expand All @@ -190,10 +75,7 @@ public <T, E extends Throwable> T startAndCall(
* @param runnable the {@link SpanRunnable} to run
* @param <E> the type of the exception
*/
@SuppressWarnings("NullAway")
public <E extends Throwable> void startAndRun(SpanRunnable<E> runnable) throws E {
startAndRun(runnable, ExtendedSpanBuilder::setSpanError);
}
<E extends Throwable> void startAndRun(SpanRunnable<E> runnable) throws E;

/**
* Runs the given {@link SpanRunnable} inside of the span created by the given {@link
Expand All @@ -206,25 +88,6 @@ public <E extends Throwable> void startAndRun(SpanRunnable<E> runnable) throws E
* @param runnable the {@link SpanRunnable} to run
* @param <E> the type of the exception
*/
@SuppressWarnings("NullAway")
public <E extends Throwable> void startAndRun(
SpanRunnable<E> runnable, BiConsumer<Span, Throwable> handleException) throws E {
startAndCall(
() -> {
runnable.runInSpan();
return null;
},
handleException);
}

/**
* Marks a span as error. This is the default exception handler.
*
* @param span the span
* @param exception the exception that caused the error
*/
private static void setSpanError(Span span, Throwable exception) {
span.setStatus(StatusCode.ERROR);
span.recordException(exception);
}
<E extends Throwable> void startAndRun(
SpanRunnable<E> runnable, BiConsumer<Span, Throwable> handleException) throws E;
}
Loading
Loading