Skip to content

Commit add40c1

Browse files
authored
Move noop-api from opentelemetry-java (open-telemetry#430)
* Move noop-api from opentelemetry-java * Move test * Spotless
1 parent 1e7f1d0 commit add40c1

File tree

9 files changed

+326
-1
lines changed

9 files changed

+326
-1
lines changed

.github/component_owners.yml

+2
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ components:
3333
- kenfinnigan
3434
micrometer-meter-provider:
3535
- HaloFour
36+
noop-api:
37+
- jack-berg
3638
runtime-attach:
3739
- iNikem
3840
- jeanbisutti

noop-api/README.md

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

noop-api/build.gradle.kts

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
plugins {
2+
id("otel.java-conventions")
3+
id("otel.publish-conventions")
4+
}
5+
6+
dependencies {
7+
api("io.opentelemetry:opentelemetry-api")
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.contrib.noopapi;
7+
8+
import io.opentelemetry.context.Context;
9+
import io.opentelemetry.context.ContextKey;
10+
import io.opentelemetry.context.ContextStorage;
11+
import io.opentelemetry.context.ContextStorageProvider;
12+
import io.opentelemetry.context.Scope;
13+
import javax.annotation.Nullable;
14+
15+
/**
16+
* A {@link ContextStorageProvider} that returns a {@link ContextStorage} which is completely no-op.
17+
*/
18+
public class NoopContextStorageProvider implements ContextStorageProvider {
19+
20+
/** Returns a no-op context storage. */
21+
@Override
22+
public ContextStorage get() {
23+
return NoopContextStorage.INSTANCE;
24+
}
25+
26+
enum NoopContextStorage implements ContextStorage {
27+
INSTANCE;
28+
29+
@Override
30+
public Scope attach(Context toAttach) {
31+
return Scope.noop();
32+
}
33+
34+
@Override
35+
public Context current() {
36+
return NoopContext.INSTANCE;
37+
}
38+
}
39+
40+
enum NoopContext implements Context {
41+
INSTANCE;
42+
43+
@Nullable
44+
@Override
45+
public <V> V get(ContextKey<V> key) {
46+
return null;
47+
}
48+
49+
@Override
50+
public <V> Context with(ContextKey<V> k1, V v1) {
51+
return this;
52+
}
53+
}
54+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.contrib.noopapi;
7+
8+
import io.opentelemetry.api.OpenTelemetry;
9+
import io.opentelemetry.api.metrics.MeterProvider;
10+
import io.opentelemetry.api.trace.SpanContext;
11+
import io.opentelemetry.api.trace.TracerProvider;
12+
import io.opentelemetry.context.propagation.ContextPropagators;
13+
14+
/**
15+
* An implementation of {@link OpenTelemetry} that is completely no-op. Unlike {@link
16+
* OpenTelemetry#noop()}, this implementation does not support in-process context propagation at
17+
* all. This means that no objects are allocated nor {@link ThreadLocal}s used in an application
18+
* using this implementation. This can be a good option for use in frameworks shared across a large
19+
* number of servers to introduce instrumentation without forcing overhead on any users of the
20+
* framework. If such overhead is not a concern, always use either {@link OpenTelemetry#noop()},
21+
* {@link OpenTelemetry#propagating(ContextPropagators)}, or the OpenTelemetry SDK.
22+
*
23+
* <p>The following code will fail because context is not mounted.
24+
*
25+
* <pre>{@code
26+
* try (Scope ignored = Context.current().with(Span.wrap(VALID_SPAN_CONTEXT).makeCurrent()) {
27+
* assert Span.current().spanContext().equals(VALID_SPAN_CONTEXT);
28+
* }
29+
* }</pre>
30+
*
31+
* <p>In most cases when instrumenting a library, the above pattern does not happen because {@link
32+
* io.opentelemetry.api.trace.Span#wrap(SpanContext)} is primarily for use in remote propagators.
33+
* The common pattern looks like
34+
*
35+
* <pre>{@code
36+
* Span span = tracer.spanBuilder().setAttribute(...).startSpan();
37+
* try (Scope ignored = Context.current().with(span).makeCurrent()) {
38+
* assert Span.current().spanContext().equals(SpanContext.getInvalid());
39+
* }
40+
* }</pre>
41+
*
42+
* <p>The above will succeed both with the {@linkplain OpenTelemetry#noop() default implementation}
43+
* and this one, but with this implementation there will be no overhead at all.
44+
*/
45+
public class NoopOpenTelemetry implements OpenTelemetry {
46+
47+
private static final OpenTelemetry INSTANCE = new NoopOpenTelemetry();
48+
49+
public static OpenTelemetry getInstance() {
50+
return INSTANCE;
51+
}
52+
53+
@Override
54+
public TracerProvider getTracerProvider() {
55+
return NoopTracerProvider.INSTANCE;
56+
}
57+
58+
@Override
59+
public MeterProvider getMeterProvider() {
60+
// Default implementation is already truly no-op.
61+
return MeterProvider.noop();
62+
}
63+
64+
@Override
65+
public ContextPropagators getPropagators() {
66+
return ContextPropagators.noop();
67+
}
68+
69+
private NoopOpenTelemetry() {}
70+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.contrib.noopapi;
7+
8+
import io.opentelemetry.api.common.AttributeKey;
9+
import io.opentelemetry.api.common.Attributes;
10+
import io.opentelemetry.api.trace.Span;
11+
import io.opentelemetry.api.trace.SpanBuilder;
12+
import io.opentelemetry.api.trace.SpanContext;
13+
import io.opentelemetry.api.trace.SpanKind;
14+
import io.opentelemetry.api.trace.Tracer;
15+
import io.opentelemetry.api.trace.TracerProvider;
16+
import io.opentelemetry.context.Context;
17+
import java.util.concurrent.TimeUnit;
18+
19+
enum NoopTracerProvider implements TracerProvider {
20+
INSTANCE;
21+
22+
@Override
23+
public Tracer get(String instrumentationScopeName) {
24+
return NoopTracer.INSTANCE;
25+
}
26+
27+
@Override
28+
public Tracer get(String instrumentationScopeName, String instrumentationScopeVersion) {
29+
return NoopTracer.INSTANCE;
30+
}
31+
32+
enum NoopTracer implements Tracer {
33+
INSTANCE;
34+
35+
@Override
36+
public SpanBuilder spanBuilder(String spanName) {
37+
return NoopSpanBuilder.INSTANCE;
38+
}
39+
}
40+
41+
enum NoopSpanBuilder implements SpanBuilder {
42+
INSTANCE;
43+
44+
@Override
45+
public SpanBuilder setParent(Context context) {
46+
return this;
47+
}
48+
49+
@Override
50+
public SpanBuilder setNoParent() {
51+
return this;
52+
}
53+
54+
@Override
55+
public SpanBuilder addLink(SpanContext spanContext) {
56+
return this;
57+
}
58+
59+
@Override
60+
public SpanBuilder addLink(SpanContext spanContext, Attributes attributes) {
61+
return this;
62+
}
63+
64+
@Override
65+
public SpanBuilder setAttribute(String key, String value) {
66+
return this;
67+
}
68+
69+
@Override
70+
public SpanBuilder setAttribute(String key, long value) {
71+
return this;
72+
}
73+
74+
@Override
75+
public SpanBuilder setAttribute(String key, double value) {
76+
return this;
77+
}
78+
79+
@Override
80+
public SpanBuilder setAttribute(String key, boolean value) {
81+
return this;
82+
}
83+
84+
@Override
85+
public <T> SpanBuilder setAttribute(AttributeKey<T> key, T value) {
86+
return this;
87+
}
88+
89+
@Override
90+
public SpanBuilder setSpanKind(SpanKind spanKind) {
91+
return this;
92+
}
93+
94+
@Override
95+
public SpanBuilder setStartTimestamp(long startTimestamp, TimeUnit unit) {
96+
return this;
97+
}
98+
99+
@Override
100+
public Span startSpan() {
101+
return Span.getInvalid();
102+
}
103+
}
104+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
io.opentelemetry.contrib.noopapi.NoopContextStorageProvider
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.contrib.noopapi;
7+
8+
import static org.assertj.core.api.Assertions.assertThat;
9+
10+
import io.opentelemetry.api.common.AttributeKey;
11+
import io.opentelemetry.api.common.Attributes;
12+
import io.opentelemetry.api.trace.Span;
13+
import io.opentelemetry.api.trace.SpanBuilder;
14+
import io.opentelemetry.api.trace.SpanContext;
15+
import io.opentelemetry.api.trace.SpanKind;
16+
import io.opentelemetry.api.trace.TraceFlags;
17+
import io.opentelemetry.api.trace.TraceState;
18+
import io.opentelemetry.context.Context;
19+
import io.opentelemetry.context.Scope;
20+
import java.util.concurrent.TimeUnit;
21+
import org.junit.jupiter.api.Test;
22+
23+
class NoopOpenTelemetryTest {
24+
25+
private static final SpanContext SPAN_CONTEXT =
26+
SpanContext.create(
27+
"00000000000000000000000000000061",
28+
"0000000000000061",
29+
TraceFlags.getDefault(),
30+
TraceState.getDefault());
31+
32+
@Test
33+
void contextNoOp() {
34+
// Context.root() is not a no-op Context, so the default context is never root.
35+
Context context = Context.current();
36+
assertThat(context).isNotSameAs(Context.root());
37+
// No allocations
38+
assertThat(context.with(Span.wrap(SPAN_CONTEXT))).isSameAs(context);
39+
assertThat(SPAN_CONTEXT.isValid()).isTrue();
40+
try (Scope ignored = Context.current().with(Span.wrap(SPAN_CONTEXT)).makeCurrent()) {
41+
// No context mounted, so always an invalid span.
42+
assertThat(Span.fromContext(Context.current()).getSpanContext().isValid()).isFalse();
43+
}
44+
}
45+
46+
@Test
47+
void tracerNoOp() {
48+
SpanBuilder span1 = NoopOpenTelemetry.getInstance().getTracer("test").spanBuilder("test");
49+
SpanBuilder span2 = NoopOpenTelemetry.getInstance().getTracer("test").spanBuilder("test");
50+
// No allocations
51+
assertThat(span1).isSameAs(span2);
52+
53+
// No crash
54+
span1.setParent(Context.current());
55+
span1.setNoParent();
56+
span1.addLink(SPAN_CONTEXT);
57+
span1.addLink(SPAN_CONTEXT, Attributes.empty());
58+
span1.setAttribute("key", "value");
59+
span1.setAttribute("key", 1L);
60+
span1.setAttribute("key", 1.0);
61+
span1.setAttribute("key", true);
62+
span1.setAttribute(AttributeKey.stringKey("key"), "value");
63+
span1.setSpanKind(SpanKind.CLIENT);
64+
span1.setStartTimestamp(1, TimeUnit.DAYS);
65+
66+
// No allocations
67+
assertThat(span1.startSpan()).isSameAs(Span.getInvalid());
68+
}
69+
}

settings.gradle.kts

+2-1
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,10 @@ include(":dependencyManagement")
4444
include(":example")
4545
include(":jfr-events")
4646
include(":jfr-streaming")
47-
include(":micrometer-meter-provider")
4847
include(":jmx-metrics")
4948
include(":maven-extension")
49+
include(":micrometer-meter-provider")
50+
include(":noop-api")
5051
include(":runtime-attach:runtime-attach")
5152
include(":runtime-attach:runtime-attach-core")
5253
include(":samplers")

0 commit comments

Comments
 (0)