diff --git a/CHANGELOG.md b/CHANGELOG.md index ffefcd416da1c..d8d68c51ceed5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -171,6 +171,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Separate request-based and settings-based concurrent segment search controls and introduce AggregatorFactory method to determine concurrent search support ([#9469](https://github.com/opensearch-project/OpenSearch/pull/9469)) - [Remote Store] Rate limiter integration for remote store uploads and downloads([#9448](https://github.com/opensearch-project/OpenSearch/pull/9448/)) - [Remote Store] Implicitly use replication type SEGMENT for remote store clusters ([#9264](https://github.com/opensearch-project/OpenSearch/pull/9264)) +- Redefine telemetry context restoration and propagation ([#9617](https://github.com/opensearch-project/OpenSearch/pull/9617)) - Use non-concurrent path for sort request on timeseries index and field([#9562](https://github.com/opensearch-project/OpenSearch/pull/9562)) - Added sampler based on `Blanket Probabilistic Sampling rate` and `Override for on demand` ([#9621](https://github.com/opensearch-project/OpenSearch/issues/9621)) diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/DefaultScopedSpan.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/DefaultScopedSpan.java new file mode 100644 index 0000000000000..87802b1f1931d --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/DefaultScopedSpan.java @@ -0,0 +1,89 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import java.util.Objects; + +/** + * Default implementation of Scope + * + * @opensearch.internal + */ +final class DefaultScopedSpan implements ScopedSpan { + + private final Span span; + + private final SpanScope spanScope; + + /** + * Creates Scope instance for the given span + * + * @param span underlying span + * @param spanScope span scope. + */ + public DefaultScopedSpan(Span span, SpanScope spanScope) { + this.span = Objects.requireNonNull(span); + this.spanScope = Objects.requireNonNull(spanScope); + } + + @Override + public void addAttribute(String key, String value) { + span.addAttribute(key, value); + } + + @Override + public void addAttribute(String key, long value) { + span.addAttribute(key, value); + } + + @Override + public void addAttribute(String key, double value) { + span.addAttribute(key, value); + } + + @Override + public void addAttribute(String key, boolean value) { + span.addAttribute(key, value); + } + + @Override + public void addEvent(String event) { + span.addEvent(event); + } + + @Override + public void setError(Exception exception) { + span.setError(exception); + } + + /** + * Executes the runnable to end the scope + */ + @Override + public void close() { + span.endSpan(); + spanScope.close(); + } + + /** + * Returns span. + * @return + */ + Span getSpan() { + return span; + } + + /** + * Returns {@link SpanScope} + * @return spanScope + */ + SpanScope getSpanScope() { + return spanScope; + } +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/DefaultSpanScope.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/DefaultSpanScope.java index 356b72187de74..037309da0bc30 100644 --- a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/DefaultSpanScope.java +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/DefaultSpanScope.java @@ -8,65 +8,68 @@ package org.opensearch.telemetry.tracing; -import java.util.function.Consumer; +import java.util.Objects; /** - * Default implementation of Scope - * - * @opensearch.internal + * Default implementation for {@link SpanScope} */ -final class DefaultSpanScope implements SpanScope { - +public class DefaultSpanScope implements SpanScope { private final Span span; - - private final Consumer onCloseConsumer; + private final SpanScope previousSpanScope; + private static final ThreadLocal spanScopeThreadLocal = new ThreadLocal<>(); + private final TracerContextStorage tracerContextStorage; /** - * Creates Scope instance for the given span - * - * @param span underlying span - * @param onCloseConsumer consumer to execute on scope close + * Constructor + * @param span span + * @param previousSpanScope before attached span scope. */ - public DefaultSpanScope(Span span, Consumer onCloseConsumer) { - this.span = span; - this.onCloseConsumer = onCloseConsumer; + private DefaultSpanScope(Span span, SpanScope previousSpanScope, TracerContextStorage tracerContextStorage) { + this.span = Objects.requireNonNull(span); + this.previousSpanScope = previousSpanScope; + this.tracerContextStorage = tracerContextStorage; } - @Override - public void addSpanAttribute(String key, String value) { - span.addAttribute(key, value); + /** + * Creates the SpanScope object. + * @param span span. + * @param tracerContextStorage tracer context storage. + * @return SpanScope spanScope + */ + public static SpanScope create(Span span, TracerContextStorage tracerContextStorage) { + final SpanScope beforeSpanScope = spanScopeThreadLocal.get(); + SpanScope newSpanScope = new DefaultSpanScope(span, beforeSpanScope, tracerContextStorage); + spanScopeThreadLocal.set(newSpanScope); + return newSpanScope; } @Override - public void addSpanAttribute(String key, long value) { - span.addAttribute(key, value); + public void close() { + detach(); + spanScopeThreadLocal.set(previousSpanScope); } @Override - public void addSpanAttribute(String key, double value) { - span.addAttribute(key, value); + public SpanScope attach() { + tracerContextStorage.put(TracerContextStorage.CURRENT_SPAN, this.span); + return this; } - @Override - public void addSpanAttribute(String key, boolean value) { - span.addAttribute(key, value); + private void detach() { + if (previousSpanScope != null) { + tracerContextStorage.put(TracerContextStorage.CURRENT_SPAN, previousSpanScope.getSpan()); + } else { + tracerContextStorage.put(TracerContextStorage.CURRENT_SPAN, null); + } } @Override - public void addSpanEvent(String event) { - span.addEvent(event); + public Span getSpan() { + return span; } - @Override - public void setError(Exception exception) { - span.setError(exception); + static SpanScope getCurrentSpanScope() { + return spanScopeThreadLocal.get(); } - /** - * Executes the runnable to end the scope - */ - @Override - public void close() { - onCloseConsumer.accept(span); - } } diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/DefaultTracer.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/DefaultTracer.java index bc1a08e2d3c72..b75d761fc240c 100644 --- a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/DefaultTracer.java +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/DefaultTracer.java @@ -41,17 +41,22 @@ public DefaultTracer(TracingTelemetry tracingTelemetry, TracerContextStorage endSpan(scopeSpan)); + return span; } @Override @@ -77,11 +82,21 @@ public SpanContext getCurrentSpan() { return (currentSpan == null) ? null : new SpanContext(currentSpan); } - private void endSpan(Span span) { - if (span != null) { - span.endSpan(); - setCurrentSpanInContext(span.getParentSpan()); - } + @Override + public ScopedSpan startScopedSpan(SpanCreationContext spanCreationContext) { + return startScopedSpan(spanCreationContext, null); + } + + @Override + public ScopedSpan startScopedSpan(SpanCreationContext spanCreationContext, SpanContext parentSpan) { + Span span = startSpan(spanCreationContext.getSpanName(), parentSpan, spanCreationContext.getAttributes()); + SpanScope spanScope = withSpanInScope(span); + return new DefaultScopedSpan(span, spanScope); + } + + @Override + public SpanScope withSpanInScope(Span span) { + return DefaultSpanScope.create(span, tracerContextStorage).attach(); } private Span createSpan(String spanName, Span parentSpan, Attributes attributes) { @@ -101,7 +116,7 @@ protected void addDefaultAttributes(Span span) { } @Override - public SpanScope startSpan(String spanName, Map> headers, Attributes attributes) { + public Span startSpan(String spanName, Map> headers, Attributes attributes) { Optional propagatedSpan = tracingTelemetry.getContextPropagator().extractFromHeaders(headers); return startSpan(spanName, propagatedSpan.map(SpanContext::new).orElse(null), attributes); } diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/ScopedSpan.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/ScopedSpan.java new file mode 100644 index 0000000000000..833a85ef27baf --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/ScopedSpan.java @@ -0,0 +1,74 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import org.opensearch.telemetry.tracing.noop.NoopScopedSpan; + +/** + * An auto-closeable that represents scoped span. + * It provides interface for all the span operations. + */ +public interface ScopedSpan extends AutoCloseable { + /** + * No-op Scope implementation + */ + ScopedSpan NO_OP = new NoopScopedSpan(); + + /** + * Adds string attribute to the {@link Span}. + * + * @param key attribute key + * @param value attribute value + */ + void addAttribute(String key, String value); + + /** + * Adds long attribute to the {@link Span}. + * + * @param key attribute key + * @param value attribute value + */ + void addAttribute(String key, long value); + + /** + * Adds double attribute to the {@link Span}. + * + * @param key attribute key + * @param value attribute value + */ + void addAttribute(String key, double value); + + /** + * Adds boolean attribute to the {@link Span}. + * + * @param key attribute key + * @param value attribute value + */ + void addAttribute(String key, boolean value); + + /** + * Adds an event to the {@link Span}. + * + * @param event event name + */ + void addEvent(String event); + + /** + * Records error in the span + * + * @param exception exception to be recorded + */ + void setError(Exception exception); + + /** + * closes the scope + */ + @Override + void close(); +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/SpanCreationContext.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/SpanCreationContext.java new file mode 100644 index 0000000000000..0f91eb448deb5 --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/SpanCreationContext.java @@ -0,0 +1,45 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import org.opensearch.telemetry.tracing.attributes.Attributes; + +/** + * Context for span details. + */ +public final class SpanCreationContext { + private final String spanName; + private final Attributes attributes; + + /** + * Constructor. + * @param spanName span name. + * @param attributes attributes. + */ + public SpanCreationContext(String spanName, Attributes attributes) { + this.spanName = spanName; + this.attributes = attributes; + } + + /** + * Returns the span name. + * @return span name + */ + public String getSpanName() { + return spanName; + } + + /** + * Returns the span attributes. + * @return attributes. + */ + public Attributes getAttributes() { + return attributes; + } +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/SpanScope.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/SpanScope.java index cf67165d889bc..99c27b72fe1c7 100644 --- a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/SpanScope.java +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/SpanScope.java @@ -12,63 +12,26 @@ /** * An auto-closeable that represents scope of the span. - * It provides interface for all the span operations. */ public interface SpanScope extends AutoCloseable { + /** * No-op Scope implementation */ SpanScope NO_OP = new NoopSpanScope(); - /** - * Adds string attribute to the {@link Span}. - * - * @param key attribute key - * @param value attribute value - */ - void addSpanAttribute(String key, String value); - - /** - * Adds long attribute to the {@link Span}. - * - * @param key attribute key - * @param value attribute value - */ - void addSpanAttribute(String key, long value); - - /** - * Adds double attribute to the {@link Span}. - * - * @param key attribute key - * @param value attribute value - */ - void addSpanAttribute(String key, double value); - - /** - * Adds boolean attribute to the {@link Span}. - * - * @param key attribute key - * @param value attribute value - */ - void addSpanAttribute(String key, boolean value); - - /** - * Adds an event to the {@link Span}. - * - * @param event event name - */ - void addSpanEvent(String event); + @Override + void close(); /** - * Records error in the span - * - * @param exception exception to be recorded + * Attaches span to the {@link SpanScope} + * @return spanScope */ - void setError(Exception exception); + SpanScope attach(); /** - * closes the scope + * Returns span attached with the {@link SpanScope} + * @return span. */ - @Override - void close(); + Span getSpan(); } diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/Tracer.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/Tracer.java index 40cc5dfd2d743..dc247e45d77f6 100644 --- a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/Tracer.java +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/Tracer.java @@ -20,36 +20,71 @@ * All methods on the Tracer object are multi-thread safe. */ public interface Tracer extends HttpTracer, Closeable { + /** + * Starts the {@link Span} with given {@link SpanCreationContext} + * + * @param context span context + * @return span, must be closed. + */ + Span startSpan(SpanCreationContext context); /** * Starts the {@link Span} with given name * * @param spanName span name - * @return scope of the span, must be closed with explicit close or with try-with-resource + * @return span, must be closed. */ - SpanScope startSpan(String spanName); + Span startSpan(String spanName); /** * Starts the {@link Span} with given name and attributes. This is required in cases when some attribute based * decision needs to be made before starting the span. Very useful in the case of Sampling. - * @param spanName span name. + * + * @param spanName span name. * @param attributes attributes to be added. - * @return scope of the span, must be closed with explicit close or with try-with-resource + * @return span, must be closed. */ - SpanScope startSpan(String spanName, Attributes attributes); + Span startSpan(String spanName, Attributes attributes); /** * Starts the {@link Span} with the given name, parent and attributes. - * @param spanName span name. + * + * @param spanName span name. * @param parentSpan parent span. * @param attributes attributes to be added. - * @return scope of the span, must be closed with explicit close or with try-with-resource + * @return span, must be closed. */ - SpanScope startSpan(String spanName, SpanContext parentSpan, Attributes attributes); + Span startSpan(String spanName, SpanContext parentSpan, Attributes attributes); /** * Returns the current span. * @return current wrapped span. */ SpanContext getCurrentSpan(); + + /** + * Start the span and scoped it. This must be used for scenarios where {@link SpanScope} and {@link Span} lifecycles + * are same and ends within the same thread where created. + * @param spanCreationContext span creation context + * @return scope of the span, must be closed with explicit close or with try-with-resource + */ + ScopedSpan startScopedSpan(SpanCreationContext spanCreationContext); + + /** + * Start the span and scoped it. This must be used for scenarios where {@link SpanScope} and {@link Span} lifecycles + * are same and ends within the same thread where created. + * @param spanCreationContext span creation context + * @param parentSpan parent span. + * @return scope of the span, must be closed with explicit close or with try-with-resource + */ + ScopedSpan startScopedSpan(SpanCreationContext spanCreationContext, SpanContext parentSpan); + + /** + * Creates the Span Scope for a current thread. It's mandatory to scope the span just after creation so that it will + * automatically manage the attach /detach to the current thread. + * @param span span to be scoped + * @return ScopedSpan + */ + SpanScope withSpanInScope(Span span); + } diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/TracingTelemetry.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/TracingTelemetry.java index 2e91cadbf395f..895e14da69f7f 100644 --- a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/TracingTelemetry.java +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/TracingTelemetry.java @@ -21,7 +21,8 @@ public interface TracingTelemetry extends Closeable { /** * Creates span with provided arguments - * @param spanName name of the span + * + * @param spanName name of the span * @param parentSpan span's parent span * @param attributes attributes to be added. * @return span instance diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/http/HttpTracer.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/http/HttpTracer.java index 64ef84335a95b..8027291d5480b 100644 --- a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/http/HttpTracer.java +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/http/HttpTracer.java @@ -9,7 +9,6 @@ package org.opensearch.telemetry.tracing.http; import org.opensearch.telemetry.tracing.Span; -import org.opensearch.telemetry.tracing.SpanScope; import org.opensearch.telemetry.tracing.attributes.Attributes; import java.util.List; @@ -28,7 +27,7 @@ public interface HttpTracer { * @param spanName span name. * @param header http request header. * @param attributes span attributes. - * @return scope of the span, must be closed with explicit close or with try-with-resource + * @return span. */ - SpanScope startSpan(String spanName, Map> header, Attributes attributes); + Span startSpan(String spanName, Map> header, Attributes attributes); } diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/noop/NoopScopedSpan.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/noop/NoopScopedSpan.java new file mode 100644 index 0000000000000..22ee7e7ccb6fb --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/noop/NoopScopedSpan.java @@ -0,0 +1,59 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing.noop; + +import org.opensearch.telemetry.tracing.ScopedSpan; + +/** + * No-op implementation of SpanScope + * + * @opensearch.internal + */ +public final class NoopScopedSpan implements ScopedSpan { + + /** + * No-args constructor + */ + public NoopScopedSpan() {} + + @Override + public void addAttribute(String key, String value) { + + } + + @Override + public void addAttribute(String key, long value) { + + } + + @Override + public void addAttribute(String key, double value) { + + } + + @Override + public void addAttribute(String key, boolean value) { + + } + + @Override + public void addEvent(String event) { + + } + + @Override + public void setError(Exception exception) { + + } + + @Override + public void close() { + + } +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/noop/NoopSpan.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/noop/NoopSpan.java new file mode 100644 index 0000000000000..e053e6576fcea --- /dev/null +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/noop/NoopSpan.java @@ -0,0 +1,81 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing.noop; + +import org.opensearch.telemetry.tracing.Span; + +/** + * No-op implementation of {@link org.opensearch.telemetry.tracing.Span} + */ +public class NoopSpan implements Span { + + /** + * No-op Span instance + */ + public final static NoopSpan INSTANCE = new NoopSpan(); + + private NoopSpan() { + + } + + @Override + public void endSpan() { + + } + + @Override + public Span getParentSpan() { + return null; + } + + @Override + public String getSpanName() { + return "noop-span"; + } + + @Override + public void addAttribute(String key, String value) { + + } + + @Override + public void addAttribute(String key, Long value) { + + } + + @Override + public void addAttribute(String key, Double value) { + + } + + @Override + public void addAttribute(String key, Boolean value) { + + } + + @Override + public void setError(Exception exception) { + + } + + @Override + public void addEvent(String event) { + + } + + @Override + public String getTraceId() { + return "noop-trace-id"; + } + + @Override + public String getSpanId() { + return "noop-span-id"; + } +} diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/noop/NoopSpanScope.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/noop/NoopSpanScope.java index a1d16d1d80d00..a9b72adeeda0e 100644 --- a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/noop/NoopSpanScope.java +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/noop/NoopSpanScope.java @@ -8,52 +8,32 @@ package org.opensearch.telemetry.tracing.noop; +import org.opensearch.telemetry.tracing.Span; import org.opensearch.telemetry.tracing.SpanScope; /** - * No-op implementation of SpanScope - * - * @opensearch.internal + * No-op implementation of {@link SpanScope} */ -public final class NoopSpanScope implements SpanScope { - +public class NoopSpanScope implements SpanScope { /** - * No-args constructor + * Constructor. */ - public NoopSpanScope() {} - - @Override - public void addSpanAttribute(String key, String value) { - - } - - @Override - public void addSpanAttribute(String key, long value) { - - } - - @Override - public void addSpanAttribute(String key, double value) { - - } - - @Override - public void addSpanAttribute(String key, boolean value) { + public NoopSpanScope() { } @Override - public void addSpanEvent(String event) { + public void close() { } @Override - public void setError(Exception exception) { - + public SpanScope attach() { + return this; } @Override - public void close() { - + public Span getSpan() { + return NoopSpan.INSTANCE; } } diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/noop/NoopTracer.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/noop/NoopTracer.java index 2ff50bf3bcb18..a9eee725b4f1c 100644 --- a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/noop/NoopTracer.java +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/noop/NoopTracer.java @@ -8,7 +8,10 @@ package org.opensearch.telemetry.tracing.noop; +import org.opensearch.telemetry.tracing.ScopedSpan; +import org.opensearch.telemetry.tracing.Span; import org.opensearch.telemetry.tracing.SpanContext; +import org.opensearch.telemetry.tracing.SpanCreationContext; import org.opensearch.telemetry.tracing.SpanScope; import org.opensearch.telemetry.tracing.Tracer; import org.opensearch.telemetry.tracing.attributes.Attributes; @@ -31,32 +34,52 @@ public class NoopTracer implements Tracer { private NoopTracer() {} @Override - public SpanScope startSpan(String spanName) { - return SpanScope.NO_OP; + public Span startSpan(SpanCreationContext context) { + return NoopSpan.INSTANCE; } @Override - public SpanScope startSpan(String spanName, Attributes attributes) { - return SpanScope.NO_OP; + public Span startSpan(String spanName) { + return NoopSpan.INSTANCE; } @Override - public SpanScope startSpan(String spanName, SpanContext parentSpan, Attributes attributes) { - return SpanScope.NO_OP; + public Span startSpan(String spanName, Attributes attributes) { + return NoopSpan.INSTANCE; + } + + @Override + public Span startSpan(String spanName, SpanContext parentSpan, Attributes attributes) { + return NoopSpan.INSTANCE; } @Override public SpanContext getCurrentSpan() { - return null; + return new SpanContext(NoopSpan.INSTANCE); } @Override - public void close() { + public ScopedSpan startScopedSpan(SpanCreationContext spanCreationContext) { + return ScopedSpan.NO_OP; + } + @Override + public ScopedSpan startScopedSpan(SpanCreationContext spanCreationContext, SpanContext parentSpan) { + return ScopedSpan.NO_OP; } @Override - public SpanScope startSpan(String spanName, Map> header, Attributes attributes) { + public SpanScope withSpanInScope(Span span) { return SpanScope.NO_OP; } + + @Override + public void close() { + + } + + @Override + public Span startSpan(String spanName, Map> header, Attributes attributes) { + return NoopSpan.INSTANCE; + } } diff --git a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/runnable/TraceableRunnable.java b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/runnable/TraceableRunnable.java index 54a5a7f1678e6..4672574e9f4ca 100644 --- a/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/runnable/TraceableRunnable.java +++ b/libs/telemetry/src/main/java/org/opensearch/telemetry/tracing/runnable/TraceableRunnable.java @@ -8,8 +8,9 @@ package org.opensearch.telemetry.tracing.runnable; +import org.opensearch.telemetry.tracing.ScopedSpan; import org.opensearch.telemetry.tracing.SpanContext; -import org.opensearch.telemetry.tracing.SpanScope; +import org.opensearch.telemetry.tracing.SpanCreationContext; import org.opensearch.telemetry.tracing.Tracer; import org.opensearch.telemetry.tracing.attributes.Attributes; @@ -41,7 +42,7 @@ public TraceableRunnable(Tracer tracer, String spanName, SpanContext parent, Att @Override public void run() { - try (SpanScope spanScope = tracer.startSpan(spanName, parent, attributes)) { + try (ScopedSpan spanScope = tracer.startScopedSpan(new SpanCreationContext(spanName, attributes), parent)) { runnable.run(); } } diff --git a/libs/telemetry/src/test/java/org/opensearch/telemetry/tracing/DefaultSpanScopeTests.java b/libs/telemetry/src/test/java/org/opensearch/telemetry/tracing/DefaultScopedSpanTests.java similarity index 50% rename from libs/telemetry/src/test/java/org/opensearch/telemetry/tracing/DefaultSpanScopeTests.java rename to libs/telemetry/src/test/java/org/opensearch/telemetry/tracing/DefaultScopedSpanTests.java index eea6b77ce6e1e..1d4871fe1419e 100644 --- a/libs/telemetry/src/test/java/org/opensearch/telemetry/tracing/DefaultSpanScopeTests.java +++ b/libs/telemetry/src/test/java/org/opensearch/telemetry/tracing/DefaultScopedSpanTests.java @@ -10,66 +10,71 @@ import org.opensearch.test.OpenSearchTestCase; -import java.util.function.Consumer; - import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -public class DefaultSpanScopeTests extends OpenSearchTestCase { +public class DefaultScopedSpanTests extends OpenSearchTestCase { @SuppressWarnings("unchecked") public void testClose() { Span mockSpan = mock(Span.class); - Consumer mockConsumer = mock(Consumer.class); - DefaultSpanScope defaultSpanScope = new DefaultSpanScope(mockSpan, mockConsumer); + SpanScope mockSpanScope = mock(SpanScope.class); + DefaultScopedSpan defaultSpanScope = new DefaultScopedSpan(mockSpan, mockSpanScope); defaultSpanScope.close(); - verify(mockConsumer).accept(mockSpan); + verify(mockSpan).endSpan(); + verify(mockSpanScope).close(); } public void testAddSpanAttributeString() { Span mockSpan = mock(Span.class); - DefaultSpanScope defaultSpanScope = new DefaultSpanScope(mockSpan, null); - defaultSpanScope.addSpanAttribute("key", "value"); + SpanScope mockSpanScope = mock(SpanScope.class); + DefaultScopedSpan defaultSpanScope = new DefaultScopedSpan(mockSpan, mockSpanScope); + defaultSpanScope.addAttribute("key", "value"); verify(mockSpan).addAttribute("key", "value"); } public void testAddSpanAttributeLong() { Span mockSpan = mock(Span.class); - DefaultSpanScope defaultSpanScope = new DefaultSpanScope(mockSpan, null); - defaultSpanScope.addSpanAttribute("key", 1L); + SpanScope mockSpanScope = mock(SpanScope.class); + DefaultScopedSpan defaultSpanScope = new DefaultScopedSpan(mockSpan, mockSpanScope); + defaultSpanScope.addAttribute("key", 1L); verify(mockSpan).addAttribute("key", 1L); } public void testAddSpanAttributeDouble() { Span mockSpan = mock(Span.class); - DefaultSpanScope defaultSpanScope = new DefaultSpanScope(mockSpan, null); - defaultSpanScope.addSpanAttribute("key", 1.0); + SpanScope mockSpanScope = mock(SpanScope.class); + DefaultScopedSpan defaultSpanScope = new DefaultScopedSpan(mockSpan, mockSpanScope); + defaultSpanScope.addAttribute("key", 1.0); verify(mockSpan).addAttribute("key", 1.0); } public void testAddSpanAttributeBoolean() { Span mockSpan = mock(Span.class); - DefaultSpanScope defaultSpanScope = new DefaultSpanScope(mockSpan, null); - defaultSpanScope.addSpanAttribute("key", true); + SpanScope mockSpanScope = mock(SpanScope.class); + DefaultScopedSpan defaultSpanScope = new DefaultScopedSpan(mockSpan, mockSpanScope); + defaultSpanScope.addAttribute("key", true); verify(mockSpan).addAttribute("key", true); } public void testAddEvent() { Span mockSpan = mock(Span.class); - DefaultSpanScope defaultSpanScope = new DefaultSpanScope(mockSpan, null); - defaultSpanScope.addSpanEvent("eventName"); + SpanScope mockSpanScope = mock(SpanScope.class); + DefaultScopedSpan defaultSpanScope = new DefaultScopedSpan(mockSpan, mockSpanScope); + defaultSpanScope.addEvent("eventName"); verify(mockSpan).addEvent("eventName"); } public void testSetError() { Span mockSpan = mock(Span.class); - DefaultSpanScope defaultSpanScope = new DefaultSpanScope(mockSpan, null); + SpanScope mockSpanScope = mock(SpanScope.class); + DefaultScopedSpan defaultSpanScope = new DefaultScopedSpan(mockSpan, mockSpanScope); Exception ex = new Exception("error"); defaultSpanScope.setError(ex); diff --git a/libs/telemetry/src/test/java/org/opensearch/telemetry/tracing/DefaultTracerTests.java b/libs/telemetry/src/test/java/org/opensearch/telemetry/tracing/DefaultTracerTests.java index 150992da06f89..5205bdfc8a031 100644 --- a/libs/telemetry/src/test/java/org/opensearch/telemetry/tracing/DefaultTracerTests.java +++ b/libs/telemetry/src/test/java/org/opensearch/telemetry/tracing/DefaultTracerTests.java @@ -10,17 +10,17 @@ import org.opensearch.common.settings.Settings; import org.opensearch.common.util.concurrent.ThreadContext; +import org.opensearch.core.action.ActionListener; +import org.opensearch.node.Node; import org.opensearch.telemetry.tracing.attributes.Attributes; import org.opensearch.test.OpenSearchTestCase; import org.opensearch.test.telemetry.tracing.MockSpan; import org.opensearch.test.telemetry.tracing.MockTracingTelemetry; -import org.junit.Assert; +import org.opensearch.threadpool.ThreadPool; import java.io.IOException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.atomic.AtomicReference; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -36,15 +36,23 @@ public class DefaultTracerTests extends OpenSearchTestCase { private Span mockSpan; private Span mockParentSpan; + private SpanScope mockSpanScope; + private ThreadPool threadPool; + private ExecutorService executorService; + @Override public void setUp() throws Exception { super.setUp(); + threadPool = new ThreadPool(Settings.builder().put(Node.NODE_NAME_SETTING.getKey(), "default tracer tests").build()); + executorService = threadPool.executor(ThreadPool.Names.GENERIC); setupMocks(); } @Override public void tearDown() throws Exception { super.tearDown(); + executorService.shutdown(); + threadPool.shutdownNow(); } public void testCreateSpan() { @@ -52,43 +60,47 @@ public void testCreateSpan() { defaultTracer.startSpan("span_name"); - Assert.assertEquals("span_name", defaultTracer.getCurrentSpan().getSpan().getSpanName()); + assertEquals("span_name", defaultTracer.getCurrentSpan().getSpan().getSpanName()); } + @SuppressWarnings("unchecked") public void testCreateSpanWithAttributesWithMock() { DefaultTracer defaultTracer = new DefaultTracer(mockTracingTelemetry, mockTracerContextStorage); Attributes attributes = Attributes.create().addAttribute("name", "value"); - when(mockTracingTelemetry.createSpan("span_name", mockParentSpan, attributes)).thenReturn(mockSpan); + when(mockTracingTelemetry.createSpan(eq("span_name"), eq(mockParentSpan), eq(attributes))).thenReturn(mockSpan); defaultTracer.startSpan("span_name", attributes); - verify(mockTracingTelemetry).createSpan("span_name", mockParentSpan, attributes); + verify(mockTracingTelemetry).createSpan(eq("span_name"), eq(mockParentSpan), eq(attributes)); } + @SuppressWarnings("unchecked") public void testCreateSpanWithAttributesWithParentMock() { DefaultTracer defaultTracer = new DefaultTracer(mockTracingTelemetry, mockTracerContextStorage); Attributes attributes = Attributes.create().addAttribute("name", "value"); - when(mockTracingTelemetry.createSpan("span_name", mockParentSpan, attributes)).thenReturn(mockSpan); + when(mockTracingTelemetry.createSpan(eq("span_name"), eq(mockParentSpan), eq(attributes))).thenReturn(mockSpan); defaultTracer.startSpan("span_name", new SpanContext(mockParentSpan), attributes); - verify(mockTracingTelemetry).createSpan("span_name", mockParentSpan, attributes); + verify(mockTracingTelemetry).createSpan(eq("span_name"), eq(mockParentSpan), eq(attributes)); verify(mockTracerContextStorage, never()).get(TracerContextStorage.CURRENT_SPAN); } public void testCreateSpanWithAttributes() { TracingTelemetry tracingTelemetry = new MockTracingTelemetry(); + ThreadContext threadContext = new ThreadContext(Settings.EMPTY); DefaultTracer defaultTracer = new DefaultTracer( tracingTelemetry, - new ThreadContextBasedTracerContextStorage(new ThreadContext(Settings.EMPTY), tracingTelemetry) + new ThreadContextBasedTracerContextStorage(threadContext, tracingTelemetry) ); - defaultTracer.startSpan( + Span span = defaultTracer.startSpan( "span_name", Attributes.create().addAttribute("key1", 1.0).addAttribute("key2", 2l).addAttribute("key3", true).addAttribute("key4", "key4") ); - Assert.assertEquals("span_name", defaultTracer.getCurrentSpan().getSpan().getSpanName()); - Assert.assertEquals(1.0, ((MockSpan) defaultTracer.getCurrentSpan().getSpan()).getAttribute("key1")); - Assert.assertEquals(2l, ((MockSpan) defaultTracer.getCurrentSpan().getSpan()).getAttribute("key2")); - Assert.assertEquals(true, ((MockSpan) defaultTracer.getCurrentSpan().getSpan()).getAttribute("key3")); - Assert.assertEquals("key4", ((MockSpan) defaultTracer.getCurrentSpan().getSpan()).getAttribute("key4")); + assertEquals("span_name", defaultTracer.getCurrentSpan().getSpan().getSpanName()); + assertEquals(1.0, ((MockSpan) defaultTracer.getCurrentSpan().getSpan()).getAttribute("key1")); + assertEquals(2l, ((MockSpan) defaultTracer.getCurrentSpan().getSpan()).getAttribute("key2")); + assertEquals(true, ((MockSpan) defaultTracer.getCurrentSpan().getSpan()).getAttribute("key3")); + assertEquals("key4", ((MockSpan) defaultTracer.getCurrentSpan().getSpan()).getAttribute("key4")); + span.endSpan(); } public void testCreateSpanWithParent() { @@ -98,57 +110,284 @@ public void testCreateSpanWithParent() { new ThreadContextBasedTracerContextStorage(new ThreadContext(Settings.EMPTY), tracingTelemetry) ); - defaultTracer.startSpan("span_name", null); + Span span = defaultTracer.startSpan("span_name", null); SpanContext parentSpan = defaultTracer.getCurrentSpan(); - defaultTracer.startSpan("span_name_1", parentSpan, Attributes.EMPTY); + Span span1 = defaultTracer.startSpan("span_name_1", parentSpan, Attributes.EMPTY); - Assert.assertEquals("span_name_1", defaultTracer.getCurrentSpan().getSpan().getSpanName()); - Assert.assertEquals(parentSpan.getSpan(), defaultTracer.getCurrentSpan().getSpan().getParentSpan()); + assertEquals("span_name_1", defaultTracer.getCurrentSpan().getSpan().getSpanName()); + assertEquals(parentSpan.getSpan(), defaultTracer.getCurrentSpan().getSpan().getParentSpan()); + span1.endSpan(); + span.endSpan(); } - public void testHttpTracer() { - String traceId = "trace_id"; - String spanId = "span_id"; - TracingTelemetry tracingTelemetry = new MockTracingTelemetry(); + @SuppressWarnings("unchecked") + public void testCreateSpanWithContext() { + DefaultTracer defaultTracer = new DefaultTracer(mockTracingTelemetry, mockTracerContextStorage); + Attributes attributes = Attributes.create().addAttribute("name", "value"); + when(mockTracingTelemetry.createSpan(eq("span_name"), eq(mockParentSpan), eq(attributes))).thenReturn(mockSpan); + defaultTracer.startSpan(new SpanCreationContext("span_name", attributes)); + verify(mockTracingTelemetry).createSpan(eq("span_name"), eq(mockParentSpan), eq(attributes)); + } + public void testCreateSpanWithNullParent() { + TracingTelemetry tracingTelemetry = new MockTracingTelemetry(); + ThreadContext threadContext = new ThreadContext(Settings.EMPTY); DefaultTracer defaultTracer = new DefaultTracer( tracingTelemetry, - new ThreadContextBasedTracerContextStorage(new ThreadContext(Settings.EMPTY), tracingTelemetry) + new ThreadContextBasedTracerContextStorage(threadContext, tracingTelemetry) ); - Map> requestHeaders = new HashMap<>(); - requestHeaders.put("traceparent", Arrays.asList(traceId + "~" + spanId)); + Span span = defaultTracer.startSpan("span_name", (SpanContext) null, Attributes.EMPTY); + + assertEquals("span_name", defaultTracer.getCurrentSpan().getSpan().getSpanName()); + assertEquals(null, defaultTracer.getCurrentSpan().getSpan().getParentSpan()); + span.endSpan(); + } + + public void testEndSpanByClosingScopedSpan() { + TracingTelemetry tracingTelemetry = new MockTracingTelemetry(); + ThreadContext threadContext = new ThreadContext(Settings.EMPTY); + ThreadContextBasedTracerContextStorage spanTracerStorage = new ThreadContextBasedTracerContextStorage( + threadContext, + tracingTelemetry + ); + DefaultTracer defaultTracer = new DefaultTracer(tracingTelemetry, spanTracerStorage); + ScopedSpan scopedSpan = defaultTracer.startScopedSpan(new SpanCreationContext("span_name", Attributes.EMPTY)); + assertEquals("span_name", defaultTracer.getCurrentSpan().getSpan().getSpanName()); + assertEquals(((DefaultScopedSpan) scopedSpan).getSpanScope(), DefaultSpanScope.getCurrentSpanScope()); + scopedSpan.close(); + assertTrue(((MockSpan) ((DefaultScopedSpan) scopedSpan).getSpan()).hasEnded()); + assertEquals(null, defaultTracer.getCurrentSpan()); + assertEquals(null, DefaultSpanScope.getCurrentSpanScope()); + + } + + public void testEndSpanByClosingScopedSpanMultiple() { + TracingTelemetry tracingTelemetry = new MockTracingTelemetry(); + ThreadContext threadContext = new ThreadContext(Settings.EMPTY); + ThreadContextBasedTracerContextStorage spanTracerStorage = new ThreadContextBasedTracerContextStorage( + threadContext, + tracingTelemetry + ); + DefaultTracer defaultTracer = new DefaultTracer(tracingTelemetry, spanTracerStorage); + ScopedSpan scopedSpan = defaultTracer.startScopedSpan(new SpanCreationContext("span_name", Attributes.EMPTY)); + ScopedSpan scopedSpan1 = defaultTracer.startScopedSpan(new SpanCreationContext("span_name_1", Attributes.EMPTY)); + + assertEquals("span_name_1", defaultTracer.getCurrentSpan().getSpan().getSpanName()); + assertEquals(((DefaultScopedSpan) scopedSpan1).getSpanScope(), DefaultSpanScope.getCurrentSpanScope()); + + scopedSpan1.close(); + assertTrue(((MockSpan) ((DefaultScopedSpan) scopedSpan1).getSpan()).hasEnded()); + assertEquals("span_name", defaultTracer.getCurrentSpan().getSpan().getSpanName()); + assertEquals(((DefaultScopedSpan) scopedSpan).getSpanScope(), DefaultSpanScope.getCurrentSpanScope()); + + scopedSpan.close(); + assertTrue(((MockSpan) ((DefaultScopedSpan) scopedSpan).getSpan()).hasEnded()); + assertEquals(null, defaultTracer.getCurrentSpan()); + assertEquals(null, DefaultSpanScope.getCurrentSpanScope()); + + } + + public void testEndSpanByClosingSpanScope() { + TracingTelemetry tracingTelemetry = new MockTracingTelemetry(); + ThreadContext threadContext = new ThreadContext(Settings.EMPTY); + ThreadContextBasedTracerContextStorage spanTracerStorage = new ThreadContextBasedTracerContextStorage( + threadContext, + tracingTelemetry + ); + DefaultTracer defaultTracer = new DefaultTracer(tracingTelemetry, spanTracerStorage); + Span span = defaultTracer.startSpan(new SpanCreationContext("span_name", Attributes.EMPTY)); + SpanScope spanScope = defaultTracer.withSpanInScope(span); + assertEquals("span_name", defaultTracer.getCurrentSpan().getSpan().getSpanName()); + assertEquals(spanScope, DefaultSpanScope.getCurrentSpanScope()); - SpanScope spanScope = defaultTracer.startSpan("test_span", requestHeaders, Attributes.EMPTY); - SpanContext currentSpan = defaultTracer.getCurrentSpan(); - assertNotNull(currentSpan); - assertEquals(traceId, currentSpan.getSpan().getTraceId()); - assertEquals(traceId, currentSpan.getSpan().getParentSpan().getTraceId()); - assertEquals(spanId, currentSpan.getSpan().getParentSpan().getSpanId()); + span.endSpan(); spanScope.close(); + assertEquals(null, defaultTracer.getCurrentSpan()); + assertTrue(((MockSpan) span).hasEnded()); + } - public void testCreateSpanWithNullParent() { + public void testEndSpanByClosingSpanScopeMultiple() { TracingTelemetry tracingTelemetry = new MockTracingTelemetry(); - DefaultTracer defaultTracer = new DefaultTracer( - tracingTelemetry, - new ThreadContextBasedTracerContextStorage(new ThreadContext(Settings.EMPTY), tracingTelemetry) + ThreadContext threadContext = new ThreadContext(Settings.EMPTY); + ThreadContextBasedTracerContextStorage spanTracerStorage = new ThreadContextBasedTracerContextStorage( + threadContext, + tracingTelemetry ); + DefaultTracer defaultTracer = new DefaultTracer(tracingTelemetry, spanTracerStorage); + Span span = defaultTracer.startSpan(new SpanCreationContext("span_name", Attributes.EMPTY)); + Span span1 = defaultTracer.startSpan(new SpanCreationContext("span_name_1", Attributes.EMPTY)); + SpanScope spanScope = defaultTracer.withSpanInScope(span); + SpanScope spanScope1 = defaultTracer.withSpanInScope(span1); + assertEquals("span_name_1", defaultTracer.getCurrentSpan().getSpan().getSpanName()); + assertEquals(spanScope1, DefaultSpanScope.getCurrentSpanScope()); + + span1.endSpan(); + spanScope1.close(); + + assertEquals("span_name", defaultTracer.getCurrentSpan().getSpan().getSpanName()); + assertEquals(spanScope, DefaultSpanScope.getCurrentSpanScope()); + assertTrue(((MockSpan) span1).hasEnded()); + assertFalse(((MockSpan) span).hasEnded()); + span.endSpan(); + spanScope.close(); - defaultTracer.startSpan("span_name"); + assertEquals(null, defaultTracer.getCurrentSpan()); + assertEquals(null, DefaultSpanScope.getCurrentSpanScope()); + assertTrue(((MockSpan) span).hasEnded()); + assertTrue(((MockSpan) span1).hasEnded()); - Assert.assertEquals("span_name", defaultTracer.getCurrentSpan().getSpan().getSpanName()); - Assert.assertEquals(null, defaultTracer.getCurrentSpan().getSpan().getParentSpan()); } - public void testEndSpanByClosingScope() { - DefaultTracer defaultTracer = new DefaultTracer(mockTracingTelemetry, mockTracerContextStorage); - try (SpanScope spanScope = defaultTracer.startSpan("span_name")) { - verify(mockTracerContextStorage).put(TracerContextStorage.CURRENT_SPAN, mockSpan); + /** + * 1. CreateSpan in ThreadA (NotScopedSpan) + * 2. create Async task and pass the span + * 3. Scope.close + * 4. verify the current_span is still the same on async thread as the 2 + * 5. verify the main thread has current span as null. + */ + public void testSpanAcrossThreads() { + TracingTelemetry tracingTelemetry = new MockTracingTelemetry(); + ThreadContext threadContext = new ThreadContext(Settings.EMPTY); + AtomicReference currentSpanRefThread1 = new AtomicReference<>(); + AtomicReference currentSpanRefThread2 = new AtomicReference<>(); + AtomicReference currentSpanRefAfterEndThread2 = new AtomicReference<>(); + + AtomicReference spanRef = new AtomicReference<>(); + AtomicReference spanT2Ref = new AtomicReference<>(); + + ThreadContextBasedTracerContextStorage spanTracerStorage = new ThreadContextBasedTracerContextStorage( + threadContext, + tracingTelemetry + ); + DefaultTracer defaultTracer = new DefaultTracer(tracingTelemetry, spanTracerStorage); + + executorService.execute(() -> { + // create a span + Span span = defaultTracer.startSpan(new SpanCreationContext("span_name_t_1", Attributes.EMPTY)); + SpanScope spanScope = defaultTracer.withSpanInScope(span); + spanRef.set(span); + + executorService.execute(() -> { + Span spanT2 = defaultTracer.startSpan(new SpanCreationContext("span_name_t_2", Attributes.EMPTY)); + SpanScope spanScopeT2 = defaultTracer.withSpanInScope(spanT2); + spanT2Ref.set(spanT2); + + currentSpanRefThread2.set(defaultTracer.getCurrentSpan().getSpan()); + + spanT2.endSpan(); + spanScopeT2.close(); + currentSpanRefAfterEndThread2.set(getCurrentSpanFromContext(defaultTracer)); + }); + spanScope.close(); + currentSpanRefThread1.set(getCurrentSpanFromContext(defaultTracer)); + }); + assertEquals(spanT2Ref.get(), currentSpanRefThread2.get()); + assertEquals(spanRef.get(), currentSpanRefAfterEndThread2.get()); + assertEquals(null, currentSpanRefThread1.get()); + } + + public void testSpanCloseOnThread2() { + TracingTelemetry tracingTelemetry = new MockTracingTelemetry(); + ThreadContext threadContext = new ThreadContext(Settings.EMPTY); + ThreadContextBasedTracerContextStorage spanTracerStorage = new ThreadContextBasedTracerContextStorage( + threadContext, + tracingTelemetry + ); + AtomicReference currentSpanRefThread1 = new AtomicReference<>(); + AtomicReference currentSpanRefThread2 = new AtomicReference<>(); + DefaultTracer defaultTracer = new DefaultTracer(tracingTelemetry, spanTracerStorage); + final Span span = defaultTracer.startSpan(new SpanCreationContext("span_name_t1", Attributes.EMPTY)); + try (SpanScope spanScope = defaultTracer.withSpanInScope(span)) { + executorService.execute(() -> async(new ActionListener() { + @Override + public void onResponse(Boolean response) { + span.endSpan(); + currentSpanRefThread2.set(defaultTracer.getCurrentSpan()); + } + + @Override + public void onFailure(Exception e) { + + } + })); + currentSpanRefThread1.set(defaultTracer.getCurrentSpan()); } - verify(mockTracerContextStorage).put(TracerContextStorage.CURRENT_SPAN, mockParentSpan); + assertEquals(null, currentSpanRefThread2.get()); + assertEquals(span, currentSpanRefThread1.get().getSpan()); + assertEquals(null, defaultTracer.getCurrentSpan()); + } + + private void async(ActionListener actionListener) { + actionListener.onResponse(true); + } + + /** + * 1. CreateSpan in ThreadA (NotScopedSpan) + * 2. create Async task and pass the span + * 3. Inside Async task start a new span. + * 4. Scope.close + * 5. Parent Scope.close + * 6. verify the current_span is still the same on async thread as the 2 + * 7. verify the main thread has current span as null. + */ + public void testSpanAcrossThreadsMultipleSpans() { + TracingTelemetry tracingTelemetry = new MockTracingTelemetry(); + ThreadContext threadContext = new ThreadContext(Settings.EMPTY); + AtomicReference currentSpanRefThread1 = new AtomicReference<>(); + AtomicReference currentSpanRefThread2 = new AtomicReference<>(); + AtomicReference currentSpanRefAfterEndThread2 = new AtomicReference<>(); + + AtomicReference parentSpanRef = new AtomicReference<>(); + AtomicReference spanRef = new AtomicReference<>(); + AtomicReference spanT2Ref = new AtomicReference<>(); + + ThreadContextBasedTracerContextStorage spanTracerStorage = new ThreadContextBasedTracerContextStorage( + threadContext, + tracingTelemetry + ); + DefaultTracer defaultTracer = new DefaultTracer(tracingTelemetry, spanTracerStorage); + + executorService.execute(() -> { + // create a parent span + Span parentSpan = defaultTracer.startSpan(new SpanCreationContext("p_span_name", Attributes.EMPTY)); + SpanScope parentSpanScope = defaultTracer.withSpanInScope(parentSpan); + parentSpanRef.set(parentSpan); + // create a span + Span span = defaultTracer.startSpan(new SpanCreationContext("span_name_t_1", Attributes.EMPTY)); + SpanScope spanScope = defaultTracer.withSpanInScope(span); + spanRef.set(span); + + executorService.execute(() -> { + Span spanT2 = defaultTracer.startSpan(new SpanCreationContext("span_name_t_2", Attributes.EMPTY)); + SpanScope spanScopeT2 = defaultTracer.withSpanInScope(spanT2); + + Span spanT21 = defaultTracer.startSpan(new SpanCreationContext("span_name_t_2", Attributes.EMPTY)); + SpanScope spanScopeT21 = defaultTracer.withSpanInScope(spanT2); + spanT2Ref.set(spanT21); + currentSpanRefThread2.set(defaultTracer.getCurrentSpan().getSpan()); + + spanT21.endSpan(); + spanScopeT21.close(); + + spanT2.endSpan(); + spanScopeT2.close(); + currentSpanRefAfterEndThread2.set(getCurrentSpanFromContext(defaultTracer)); + }); + spanScope.close(); + parentSpanScope.close(); + currentSpanRefThread1.set(getCurrentSpanFromContext(defaultTracer)); + }); + assertEquals(spanT2Ref.get(), currentSpanRefThread2.get()); + assertEquals(spanRef.get(), currentSpanRefAfterEndThread2.get()); + assertEquals(null, currentSpanRefThread1.get()); + } + + private static Span getCurrentSpanFromContext(DefaultTracer defaultTracer) { + return defaultTracer.getCurrentSpan() != null ? defaultTracer.getCurrentSpan().getSpan() : null; } public void testClose() throws IOException { @@ -164,6 +403,7 @@ private void setupMocks() { mockTracingTelemetry = mock(TracingTelemetry.class); mockSpan = mock(Span.class); mockParentSpan = mock(Span.class); + mockSpanScope = mock(SpanScope.class); mockTracerContextStorage = mock(TracerContextStorage.class); when(mockSpan.getSpanName()).thenReturn("span_name"); when(mockSpan.getSpanId()).thenReturn("span_id"); diff --git a/libs/telemetry/src/test/java/org/opensearch/telemetry/tracing/TraceableRunnableTests.java b/libs/telemetry/src/test/java/org/opensearch/telemetry/tracing/TraceableRunnableTests.java index bcd8ffe41a17b..a67d9b22ca738 100644 --- a/libs/telemetry/src/test/java/org/opensearch/telemetry/tracing/TraceableRunnableTests.java +++ b/libs/telemetry/src/test/java/org/opensearch/telemetry/tracing/TraceableRunnableTests.java @@ -28,42 +28,46 @@ public class TraceableRunnableTests extends OpenSearchTestCase { public void testRunnableWithNullParent() throws Exception { String spanName = "testRunnable"; - DefaultTracer defaultTracer = new DefaultTracer(new MockTracingTelemetry(), contextStorage); + final DefaultTracer defaultTracer = new DefaultTracer(new MockTracingTelemetry(), contextStorage); final AtomicBoolean isRunnableCompleted = new AtomicBoolean(false); - + final AtomicReference spanNameCaptured = new AtomicReference<>(); + final AtomicReference attributeValue = new AtomicReference<>(); TraceableRunnable traceableRunnable = new TraceableRunnable( defaultTracer, spanName, null, Attributes.create().addAttribute("name", "value"), () -> { + spanNameCaptured.set(defaultTracer.getCurrentSpan().getSpan().getSpanName()); + attributeValue.set((String) ((MockSpan) defaultTracer.getCurrentSpan().getSpan()).getAttribute("name")); isRunnableCompleted.set(true); } ); traceableRunnable.run(); assertTrue(isRunnableCompleted.get()); - assertEquals(spanName, defaultTracer.getCurrentSpan().getSpan().getSpanName()); - assertEquals(null, defaultTracer.getCurrentSpan().getSpan().getParentSpan()); - assertEquals("value", ((MockSpan) defaultTracer.getCurrentSpan().getSpan()).getAttribute("name")); - + assertEquals(spanName, spanNameCaptured.get()); + assertEquals(null, defaultTracer.getCurrentSpan()); + assertEquals(null, defaultTracer.getCurrentSpan()); + assertEquals("value", attributeValue.get()); } public void testRunnableWithParent() throws Exception { String spanName = "testRunnable"; String parentSpanName = "parentSpan"; DefaultTracer defaultTracer = new DefaultTracer(new MockTracingTelemetry(), contextStorage); - defaultTracer.startSpan(parentSpanName); - SpanContext parentSpan = defaultTracer.getCurrentSpan(); + ScopedSpan scopedSpan = defaultTracer.startScopedSpan(new SpanCreationContext(parentSpanName, Attributes.EMPTY)); + SpanContext parentSpanContext = defaultTracer.getCurrentSpan(); AtomicReference currentSpan = new AtomicReference<>(); final AtomicBoolean isRunnableCompleted = new AtomicBoolean(false); - TraceableRunnable traceableRunnable = new TraceableRunnable(defaultTracer, spanName, parentSpan, Attributes.EMPTY, () -> { + TraceableRunnable traceableRunnable = new TraceableRunnable(defaultTracer, spanName, parentSpanContext, Attributes.EMPTY, () -> { isRunnableCompleted.set(true); currentSpan.set(defaultTracer.getCurrentSpan()); }); traceableRunnable.run(); assertTrue(isRunnableCompleted.get()); assertEquals(spanName, currentSpan.get().getSpan().getSpanName()); - assertEquals(parentSpan.getSpan(), currentSpan.get().getSpan().getParentSpan()); - assertEquals(parentSpan.getSpan(), defaultTracer.getCurrentSpan().getSpan()); + assertEquals(((DefaultScopedSpan) scopedSpan).getSpan(), currentSpan.get().getSpan().getParentSpan()); + assertEquals(((DefaultScopedSpan) scopedSpan).getSpan(), defaultTracer.getCurrentSpan().getSpan()); + scopedSpan.close(); } } diff --git a/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/OTelSpan.java b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/OTelSpan.java index ba63df4ae47a1..7653a56cbb740 100644 --- a/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/OTelSpan.java +++ b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/OTelSpan.java @@ -19,6 +19,12 @@ class OTelSpan extends AbstractSpan { private final Span delegateSpan; + /** + * Constructor + * @param spanName + * @param span + * @param parentSpan + */ public OTelSpan(String spanName, Span span, org.opensearch.telemetry.tracing.Span parentSpan) { super(spanName, parentSpan); this.delegateSpan = span; diff --git a/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/OTelTracingTelemetry.java b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/OTelTracingTelemetry.java index 9a3a10e63503e..ee2f17aabb7f9 100644 --- a/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/OTelTracingTelemetry.java +++ b/plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/tracing/OTelTracingTelemetry.java @@ -58,7 +58,8 @@ public TracingContextPropagator getContextPropagator() { private Span createOtelSpan(String spanName, Span parentSpan, Attributes attributes) { io.opentelemetry.api.trace.Span otelSpan = otelSpan(spanName, parentSpan, OTelAttributesConverter.convert(attributes)); - return new OTelSpan(spanName, otelSpan, parentSpan); + Span newSpan = new OTelSpan(spanName, otelSpan, parentSpan); + return newSpan; } io.opentelemetry.api.trace.Span otelSpan(String spanName, Span parentOTelSpan, io.opentelemetry.api.common.Attributes attributes) { diff --git a/server/src/main/java/org/opensearch/telemetry/tracing/AttributeNames.java b/server/src/main/java/org/opensearch/telemetry/tracing/AttributeNames.java new file mode 100644 index 0000000000000..073009ce12128 --- /dev/null +++ b/server/src/main/java/org/opensearch/telemetry/tracing/AttributeNames.java @@ -0,0 +1,62 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +/** + * Hold the Attribute names to avoid the duplication and consistensy. + */ +public final class AttributeNames { + + /** + * Constructor + */ + private AttributeNames() { + + } + + /** + * HTTP Protocol Version + */ + public static final String HTTP_PROTOCOL_VERSION = "http.version"; + + /** + * HTTP method + */ + public static final String HTTP_METHOD = "http.method"; + + /** + * HTTP Request URI. + */ + public static final String HTTP_URI = "http.uri"; + + /** + * Rest Request ID. + */ + public static final String REST_REQ_ID = "rest.request_id"; + + /** + * Rest Request Raw Path. + */ + public static final String REST_REQ_RAW_PATH = "rest.raw_path"; + + /** + * Trace key. To be used for on demand sampling. + */ + public static final String TRACE = "trace"; + + /** + * Transport Service send request target host. + */ + public static final String TRANSPORT_TARGET_HOST = "target_host"; + + /** + * Action Name. + */ + public static final String TRANSPORT_ACTION = "action"; +} diff --git a/server/src/main/java/org/opensearch/telemetry/tracing/SpanBuilder.java b/server/src/main/java/org/opensearch/telemetry/tracing/SpanBuilder.java new file mode 100644 index 0000000000000..78924febd3af5 --- /dev/null +++ b/server/src/main/java/org/opensearch/telemetry/tracing/SpanBuilder.java @@ -0,0 +1,124 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import org.opensearch.core.common.Strings; +import org.opensearch.http.HttpRequest; +import org.opensearch.rest.RestRequest; +import org.opensearch.telemetry.tracing.attributes.Attributes; +import org.opensearch.transport.Transport; + +import java.util.Arrays; +import java.util.List; + +/** + * Utility class, helps in creating the {@link SpanCreationContext} for span. + */ +public final class SpanBuilder { + + private static final List HEADERS_TO_BE_ADDED_AS_ATTRIBUTES = Arrays.asList(AttributeNames.TRACE); + /** + * Attribute name Separator + */ + private static final String SEPARATOR = " "; + + /** + * Constructor + */ + private SpanBuilder() { + + } + + /** + * Creates {@link SpanCreationContext} from the {@link HttpRequest} + * @param request Http request. + * @return context. + */ + public static SpanCreationContext from(HttpRequest request) { + return new SpanCreationContext(createSpanName(request), buildSpanAttributes(request)); + } + + /** + * Creates {@link SpanCreationContext} from the {@link RestRequest} + * @param request Rest request + * @return context + */ + public static SpanCreationContext from(RestRequest request) { + return new SpanCreationContext(createSpanName(request), buildSpanAttributes(request)); + } + + /** + * Creates {@link SpanCreationContext} from Transport action and connection details. + * @param action action. + * @param connection transport connection. + * @return context + */ + public static SpanCreationContext from(String action, Transport.Connection connection) { + return new SpanCreationContext(createSpanName(action, connection), buildSpanAttributes(action, connection)); + } + + private static String createSpanName(HttpRequest httpRequest) { + return httpRequest.method().name() + SEPARATOR + httpRequest.uri(); + } + + private static Attributes buildSpanAttributes(HttpRequest httpRequest) { + Attributes attributes = Attributes.create() + .addAttribute(AttributeNames.HTTP_URI, httpRequest.uri()) + .addAttribute(AttributeNames.HTTP_METHOD, httpRequest.method().name()) + .addAttribute(AttributeNames.HTTP_PROTOCOL_VERSION, httpRequest.protocolVersion().name()); + populateHeader(httpRequest, attributes); + return attributes; + } + + private static void populateHeader(HttpRequest httpRequest, Attributes attributes) { + HEADERS_TO_BE_ADDED_AS_ATTRIBUTES.forEach(x -> { + if (httpRequest.getHeaders() != null && httpRequest.getHeaders().get(x) != null) { + attributes.addAttribute(x, Strings.collectionToCommaDelimitedString(httpRequest.getHeaders().get(x))); + } + }); + } + + private static String createSpanName(RestRequest restRequest) { + String spanName = "rest_request"; + if (restRequest != null) { + try { + String methodName = restRequest.method().name(); + // path() does the decoding, which may give error + String path = restRequest.path(); + spanName = methodName + SEPARATOR + path; + } catch (Exception e) { + // swallow the exception and keep the default name. + } + } + return spanName; + } + + private static Attributes buildSpanAttributes(RestRequest restRequest) { + if (restRequest != null) { + return Attributes.create() + .addAttribute(AttributeNames.REST_REQ_ID, restRequest.getRequestId()) + .addAttribute(AttributeNames.REST_REQ_RAW_PATH, restRequest.rawPath()); + } else { + return Attributes.EMPTY; + } + } + + private static String createSpanName(String action, Transport.Connection connection) { + return action + SEPARATOR + (connection.getNode() != null ? connection.getNode().getHostAddress() : null); + } + + private static Attributes buildSpanAttributes(String action, Transport.Connection connection) { + Attributes attributes = Attributes.create().addAttribute(AttributeNames.TRANSPORT_ACTION, action); + if (connection != null && connection.getNode() != null) { + attributes.addAttribute(AttributeNames.TRANSPORT_TARGET_HOST, connection.getNode().getHostAddress()); + } + return attributes; + } + +} diff --git a/server/src/main/java/org/opensearch/telemetry/tracing/ThreadContextBasedTracerContextStorage.java b/server/src/main/java/org/opensearch/telemetry/tracing/ThreadContextBasedTracerContextStorage.java index a32facdc71146..8e50dac169efd 100644 --- a/server/src/main/java/org/opensearch/telemetry/tracing/ThreadContextBasedTracerContextStorage.java +++ b/server/src/main/java/org/opensearch/telemetry/tracing/ThreadContextBasedTracerContextStorage.java @@ -40,9 +40,6 @@ public Span get(String key) { @Override public void put(String key, Span span) { - if (span == null) { - return; - } SpanReference currentSpanRef = threadContext.getTransient(key); if (currentSpanRef == null) { threadContext.putTransient(key, new SpanReference(span)); diff --git a/server/src/main/java/org/opensearch/telemetry/tracing/WrappedTracer.java b/server/src/main/java/org/opensearch/telemetry/tracing/WrappedTracer.java index b699471be7f4c..1fb1eed98f5bb 100644 --- a/server/src/main/java/org/opensearch/telemetry/tracing/WrappedTracer.java +++ b/server/src/main/java/org/opensearch/telemetry/tracing/WrappedTracer.java @@ -38,12 +38,17 @@ public WrappedTracer(TelemetrySettings telemetrySettings, Tracer defaultTracer) } @Override - public SpanScope startSpan(String spanName) { + public Span startSpan(SpanCreationContext context) { + return startSpan(context.getSpanName(), context.getAttributes()); + } + + @Override + public Span startSpan(String spanName) { return startSpan(spanName, Attributes.EMPTY); } @Override - public SpanScope startSpan(String spanName, Attributes attributes) { + public Span startSpan(String spanName, Attributes attributes) { return startSpan(spanName, (SpanContext) null, attributes); } @@ -54,7 +59,22 @@ public SpanContext getCurrentSpan() { } @Override - public SpanScope startSpan(String spanName, SpanContext parentSpan, Attributes attributes) { + public ScopedSpan startScopedSpan(SpanCreationContext spanCreationContext) { + return startScopedSpan(spanCreationContext, null); + } + + @Override + public ScopedSpan startScopedSpan(SpanCreationContext spanCreationContext, SpanContext parentSpan) { + return getDelegateTracer().startScopedSpan(spanCreationContext, parentSpan); + } + + @Override + public SpanScope withSpanInScope(Span span) { + return getDelegateTracer().withSpanInScope(span); + } + + @Override + public Span startSpan(String spanName, SpanContext parentSpan, Attributes attributes) { Tracer delegateTracer = getDelegateTracer(); return delegateTracer.startSpan(spanName, parentSpan, attributes); } @@ -70,7 +90,7 @@ Tracer getDelegateTracer() { } @Override - public SpanScope startSpan(String spanName, Map> headers, Attributes attributes) { + public Span startSpan(String spanName, Map> headers, Attributes attributes) { return defaultTracer.startSpan(spanName, headers, attributes); } } diff --git a/server/src/test/java/org/opensearch/telemetry/tracing/SpanBuilderTests.java b/server/src/test/java/org/opensearch/telemetry/tracing/SpanBuilderTests.java new file mode 100644 index 0000000000000..b4183412cdf02 --- /dev/null +++ b/server/src/test/java/org/opensearch/telemetry/tracing/SpanBuilderTests.java @@ -0,0 +1,158 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.telemetry.tracing; + +import org.opensearch.Version; +import org.opensearch.cluster.node.DiscoveryNode; +import org.opensearch.common.network.NetworkAddress; +import org.opensearch.core.action.ActionListener; +import org.opensearch.core.common.bytes.BytesReference; +import org.opensearch.core.common.transport.TransportAddress; +import org.opensearch.core.rest.RestStatus; +import org.opensearch.http.HttpRequest; +import org.opensearch.http.HttpResponse; +import org.opensearch.rest.RestRequest; +import org.opensearch.telemetry.tracing.attributes.Attributes; +import org.opensearch.test.OpenSearchTestCase; +import org.opensearch.transport.Transport; +import org.opensearch.transport.TransportException; +import org.opensearch.transport.TransportRequest; +import org.opensearch.transport.TransportRequestOptions; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +public class SpanBuilderTests extends OpenSearchTestCase { + + public void testHttpRequestContext() { + HttpRequest httpRequest = createHttpRequest(); + SpanCreationContext context = SpanBuilder.from(httpRequest); + Attributes attributes = context.getAttributes(); + assertEquals("GET /_test", context.getSpanName()); + assertEquals("true", attributes.getAttributesMap().get(AttributeNames.TRACE)); + assertEquals("GET", attributes.getAttributesMap().get(AttributeNames.HTTP_METHOD)); + assertEquals("HTTP_1_0", attributes.getAttributesMap().get(AttributeNames.HTTP_PROTOCOL_VERSION)); + assertEquals("/_test", attributes.getAttributesMap().get(AttributeNames.HTTP_URI)); + } + + public void testRestRequestContext() { + RestRequest restRequest = RestRequest.request(null, createHttpRequest(), null); + SpanCreationContext context = SpanBuilder.from(restRequest); + Attributes attributes = context.getAttributes(); + assertEquals("GET /_test", context.getSpanName()); + assertEquals("/_test", attributes.getAttributesMap().get(AttributeNames.REST_REQ_RAW_PATH)); + assertNotNull(attributes.getAttributesMap().get(AttributeNames.REST_REQ_ID)); + } + + public void testRestRequestContextForNull() { + SpanCreationContext context = SpanBuilder.from((RestRequest) null); + assertEquals("rest_request", context.getSpanName()); + assertEquals(Attributes.EMPTY, context.getAttributes()); + } + + public void testTransportContext() { + String action = "test-action"; + Transport.Connection connection = createTransportConnection(); + SpanCreationContext context = SpanBuilder.from(action, connection); + Attributes attributes = context.getAttributes(); + assertEquals(action + " " + NetworkAddress.format(TransportAddress.META_ADDRESS), context.getSpanName()); + assertEquals(connection.getNode().getHostAddress(), attributes.getAttributesMap().get(AttributeNames.TRANSPORT_TARGET_HOST)); + } + + private static Transport.Connection createTransportConnection() { + return new Transport.Connection() { + @Override + public DiscoveryNode getNode() { + return new DiscoveryNode("local", new TransportAddress(TransportAddress.META_ADDRESS, 9200), Version.V_2_0_0); + } + + @Override + public void sendRequest(long requestId, String action, TransportRequest request, TransportRequestOptions options) + throws IOException, TransportException { + + } + + @Override + public void addCloseListener(ActionListener listener) { + + } + + @Override + public boolean isClosed() { + return false; + } + + @Override + public void close() { + + } + }; + } + + private static HttpRequest createHttpRequest() { + return new HttpRequest() { + @Override + public RestRequest.Method method() { + return RestRequest.Method.GET; + } + + @Override + public String uri() { + return "/_test"; + } + + @Override + public BytesReference content() { + return null; + } + + @Override + public Map> getHeaders() { + return Map.of("trace", Arrays.asList("true")); + } + + @Override + public List strictCookies() { + return null; + } + + @Override + public HttpVersion protocolVersion() { + return HttpVersion.HTTP_1_0; + } + + @Override + public HttpRequest removeHeader(String header) { + return null; + } + + @Override + public HttpResponse createResponse(RestStatus status, BytesReference content) { + return null; + } + + @Override + public Exception getInboundException() { + return null; + } + + @Override + public void release() { + + } + + @Override + public HttpRequest releaseAndCopy() { + return null; + } + }; + } +} diff --git a/server/src/test/java/org/opensearch/telemetry/tracing/TracerFactoryTests.java b/server/src/test/java/org/opensearch/telemetry/tracing/TracerFactoryTests.java index 451a1b9e3eb9c..f202be70c9425 100644 --- a/server/src/test/java/org/opensearch/telemetry/tracing/TracerFactoryTests.java +++ b/server/src/test/java/org/opensearch/telemetry/tracing/TracerFactoryTests.java @@ -15,6 +15,8 @@ import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.telemetry.Telemetry; import org.opensearch.telemetry.TelemetrySettings; +import org.opensearch.telemetry.tracing.attributes.Attributes; +import org.opensearch.telemetry.tracing.noop.NoopSpan; import org.opensearch.telemetry.tracing.noop.NoopTracer; import org.opensearch.test.OpenSearchTestCase; import org.junit.After; @@ -46,7 +48,23 @@ public void testGetTracerWithUnavailableTracingTelemetryReturnsNoopTracer() { Tracer tracer = tracerFactory.getTracer(); assertTrue(tracer instanceof NoopTracer); - assertTrue(tracer.startSpan("foo") == SpanScope.NO_OP); + assertTrue(tracer.startSpan("foo") == NoopSpan.INSTANCE); + assertTrue(tracer.startScopedSpan(new SpanCreationContext("foo", Attributes.EMPTY)) == ScopedSpan.NO_OP); + assertTrue(tracer.startScopedSpan(new SpanCreationContext("foo", Attributes.EMPTY)) == ScopedSpan.NO_OP); + assertTrue(tracer.withSpanInScope(tracer.startSpan("foo")) == SpanScope.NO_OP); + } + + public void testGetTracerWithUnavailableTracingTelemetry() { + Settings settings = Settings.builder().put(TelemetrySettings.TRACER_ENABLED_SETTING.getKey(), false).build(); + TelemetrySettings telemetrySettings = new TelemetrySettings(settings, new ClusterSettings(settings, getClusterSettings())); + Telemetry mockTelemetry = mock(Telemetry.class); + when(mockTelemetry.getTracingTelemetry()).thenReturn(mock(TracingTelemetry.class)); + tracerFactory = new TracerFactory(telemetrySettings, Optional.empty(), new ThreadContext(Settings.EMPTY)); + + Tracer tracer = tracerFactory.getTracer(); + + assertTrue(tracer instanceof NoopTracer); + assertTrue(tracer.startScopedSpan(new SpanCreationContext("foo", Attributes.EMPTY)) == ScopedSpan.NO_OP); } public void testGetTracerWithAvailableTracingTelemetryReturnsWrappedTracer() { diff --git a/test/telemetry/src/main/java/org/opensearch/test/telemetry/tracing/MockSpan.java b/test/telemetry/src/main/java/org/opensearch/test/telemetry/tracing/MockSpan.java index c22a395a6e17c..892c1a8b3eeae 100644 --- a/test/telemetry/src/main/java/org/opensearch/test/telemetry/tracing/MockSpan.java +++ b/test/telemetry/src/main/java/org/opensearch/test/telemetry/tracing/MockSpan.java @@ -36,9 +36,9 @@ public class MockSpan extends AbstractSpan { /** * Base Constructor. - * @param spanName span name - * @param parentSpan parent span - * @param spanProcessor span processor + * @param spanName Span Name + * @param parentSpan Parent Span + * @param spanProcessor Span Processor * @param attributes attributes */ public MockSpan(String spanName, Span parentSpan, SpanProcessor spanProcessor, Attributes attributes) {