Move noop-api from opentelemetry-java (#430)

* Move noop-api from opentelemetry-java

* Move test

* Spotless
This commit is contained in:
jack-berg 2022-08-29 10:21:10 -05:00 committed by GitHub
parent 1e7f1d0e20
commit add40c1fd8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 326 additions and 1 deletions

View File

@ -33,6 +33,8 @@ components:
- kenfinnigan
micrometer-meter-provider:
- HaloFour
noop-api:
- jack-berg
runtime-attach:
- iNikem
- jeanbisutti

16
noop-api/README.md Normal file
View File

@ -0,0 +1,16 @@
# OpenTelemetry Noop API
[![Javadocs][javadoc-image]][javadoc-url]
An implementation of `OpenTelemetry` that is completely no-op. Unlike `OpenTelemetry#noop()`, this
implementation does not support in-process context propagation at all. This means that no objects
are allocated nor {@link ThreadLocal}s used in an application using this implementation.
## Component owners
- [Jack Berg](https://github.com/jack-berg), New Relic
Learn more about component owners in [component_owners.yml](../.github/component_owners.yml).
[javadoc-image]: https://www.javadoc.io/badge/io.opentelemetry/opentelemetry-extension-noop-api.svg
[javadoc-url]: https://www.javadoc.io/doc/io.opentelemetry/opentelemetry-extension-noop-api

View File

@ -0,0 +1,8 @@
plugins {
id("otel.java-conventions")
id("otel.publish-conventions")
}
dependencies {
api("io.opentelemetry:opentelemetry-api")
}

View File

@ -0,0 +1,54 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.contrib.noopapi;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.ContextKey;
import io.opentelemetry.context.ContextStorage;
import io.opentelemetry.context.ContextStorageProvider;
import io.opentelemetry.context.Scope;
import javax.annotation.Nullable;
/**
* A {@link ContextStorageProvider} that returns a {@link ContextStorage} which is completely no-op.
*/
public class NoopContextStorageProvider implements ContextStorageProvider {
/** Returns a no-op context storage. */
@Override
public ContextStorage get() {
return NoopContextStorage.INSTANCE;
}
enum NoopContextStorage implements ContextStorage {
INSTANCE;
@Override
public Scope attach(Context toAttach) {
return Scope.noop();
}
@Override
public Context current() {
return NoopContext.INSTANCE;
}
}
enum NoopContext implements Context {
INSTANCE;
@Nullable
@Override
public <V> V get(ContextKey<V> key) {
return null;
}
@Override
public <V> Context with(ContextKey<V> k1, V v1) {
return this;
}
}
}

View File

@ -0,0 +1,70 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.contrib.noopapi;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.metrics.MeterProvider;
import io.opentelemetry.api.trace.SpanContext;
import io.opentelemetry.api.trace.TracerProvider;
import io.opentelemetry.context.propagation.ContextPropagators;
/**
* An implementation of {@link OpenTelemetry} that is completely no-op. Unlike {@link
* OpenTelemetry#noop()}, this implementation does not support in-process context propagation at
* all. This means that no objects are allocated nor {@link ThreadLocal}s used in an application
* using this implementation. This can be a good option for use in frameworks shared across a large
* number of servers to introduce instrumentation without forcing overhead on any users of the
* framework. If such overhead is not a concern, always use either {@link OpenTelemetry#noop()},
* {@link OpenTelemetry#propagating(ContextPropagators)}, or the OpenTelemetry SDK.
*
* <p>The following code will fail because context is not mounted.
*
* <pre>{@code
* try (Scope ignored = Context.current().with(Span.wrap(VALID_SPAN_CONTEXT).makeCurrent()) {
* assert Span.current().spanContext().equals(VALID_SPAN_CONTEXT);
* }
* }</pre>
*
* <p>In most cases when instrumenting a library, the above pattern does not happen because {@link
* io.opentelemetry.api.trace.Span#wrap(SpanContext)} is primarily for use in remote propagators.
* The common pattern looks like
*
* <pre>{@code
* Span span = tracer.spanBuilder().setAttribute(...).startSpan();
* try (Scope ignored = Context.current().with(span).makeCurrent()) {
* assert Span.current().spanContext().equals(SpanContext.getInvalid());
* }
* }</pre>
*
* <p>The above will succeed both with the {@linkplain OpenTelemetry#noop() default implementation}
* and this one, but with this implementation there will be no overhead at all.
*/
public class NoopOpenTelemetry implements OpenTelemetry {
private static final OpenTelemetry INSTANCE = new NoopOpenTelemetry();
public static OpenTelemetry getInstance() {
return INSTANCE;
}
@Override
public TracerProvider getTracerProvider() {
return NoopTracerProvider.INSTANCE;
}
@Override
public MeterProvider getMeterProvider() {
// Default implementation is already truly no-op.
return MeterProvider.noop();
}
@Override
public ContextPropagators getPropagators() {
return ContextPropagators.noop();
}
private NoopOpenTelemetry() {}
}

View File

@ -0,0 +1,104 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.contrib.noopapi;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
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.Tracer;
import io.opentelemetry.api.trace.TracerProvider;
import io.opentelemetry.context.Context;
import java.util.concurrent.TimeUnit;
enum NoopTracerProvider implements TracerProvider {
INSTANCE;
@Override
public Tracer get(String instrumentationScopeName) {
return NoopTracer.INSTANCE;
}
@Override
public Tracer get(String instrumentationScopeName, String instrumentationScopeVersion) {
return NoopTracer.INSTANCE;
}
enum NoopTracer implements Tracer {
INSTANCE;
@Override
public SpanBuilder spanBuilder(String spanName) {
return NoopSpanBuilder.INSTANCE;
}
}
enum NoopSpanBuilder implements SpanBuilder {
INSTANCE;
@Override
public SpanBuilder setParent(Context context) {
return this;
}
@Override
public SpanBuilder setNoParent() {
return this;
}
@Override
public SpanBuilder addLink(SpanContext spanContext) {
return this;
}
@Override
public SpanBuilder addLink(SpanContext spanContext, Attributes attributes) {
return this;
}
@Override
public SpanBuilder setAttribute(String key, String value) {
return this;
}
@Override
public SpanBuilder setAttribute(String key, long value) {
return this;
}
@Override
public SpanBuilder setAttribute(String key, double value) {
return this;
}
@Override
public SpanBuilder setAttribute(String key, boolean value) {
return this;
}
@Override
public <T> SpanBuilder setAttribute(AttributeKey<T> key, T value) {
return this;
}
@Override
public SpanBuilder setSpanKind(SpanKind spanKind) {
return this;
}
@Override
public SpanBuilder setStartTimestamp(long startTimestamp, TimeUnit unit) {
return this;
}
@Override
public Span startSpan() {
return Span.getInvalid();
}
}
}

View File

@ -0,0 +1 @@
io.opentelemetry.contrib.noopapi.NoopContextStorageProvider

View File

@ -0,0 +1,69 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.contrib.noopapi;
import static org.assertj.core.api.Assertions.assertThat;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
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.TraceFlags;
import io.opentelemetry.api.trace.TraceState;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Test;
class NoopOpenTelemetryTest {
private static final SpanContext SPAN_CONTEXT =
SpanContext.create(
"00000000000000000000000000000061",
"0000000000000061",
TraceFlags.getDefault(),
TraceState.getDefault());
@Test
void contextNoOp() {
// Context.root() is not a no-op Context, so the default context is never root.
Context context = Context.current();
assertThat(context).isNotSameAs(Context.root());
// No allocations
assertThat(context.with(Span.wrap(SPAN_CONTEXT))).isSameAs(context);
assertThat(SPAN_CONTEXT.isValid()).isTrue();
try (Scope ignored = Context.current().with(Span.wrap(SPAN_CONTEXT)).makeCurrent()) {
// No context mounted, so always an invalid span.
assertThat(Span.fromContext(Context.current()).getSpanContext().isValid()).isFalse();
}
}
@Test
void tracerNoOp() {
SpanBuilder span1 = NoopOpenTelemetry.getInstance().getTracer("test").spanBuilder("test");
SpanBuilder span2 = NoopOpenTelemetry.getInstance().getTracer("test").spanBuilder("test");
// No allocations
assertThat(span1).isSameAs(span2);
// No crash
span1.setParent(Context.current());
span1.setNoParent();
span1.addLink(SPAN_CONTEXT);
span1.addLink(SPAN_CONTEXT, Attributes.empty());
span1.setAttribute("key", "value");
span1.setAttribute("key", 1L);
span1.setAttribute("key", 1.0);
span1.setAttribute("key", true);
span1.setAttribute(AttributeKey.stringKey("key"), "value");
span1.setSpanKind(SpanKind.CLIENT);
span1.setStartTimestamp(1, TimeUnit.DAYS);
// No allocations
assertThat(span1.startSpan()).isSameAs(Span.getInvalid());
}
}

View File

@ -44,9 +44,10 @@ include(":dependencyManagement")
include(":example")
include(":jfr-events")
include(":jfr-streaming")
include(":micrometer-meter-provider")
include(":jmx-metrics")
include(":maven-extension")
include(":micrometer-meter-provider")
include(":noop-api")
include(":runtime-attach:runtime-attach")
include(":runtime-attach:runtime-attach-core")
include(":samplers")