You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by st...@apache.org on 2023/08/17 12:54:57 UTC

[solr] branch main updated: SOLR-16536 Replace OpenTracing instrumentation with OpenTelemetry (#1841)

This is an automated email from the ASF dual-hosted git repository.

stillalex pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr.git


The following commit(s) were added to refs/heads/main by this push:
     new 92dede18504 SOLR-16536 Replace OpenTracing instrumentation with OpenTelemetry (#1841)
92dede18504 is described below

commit 92dede18504518a5542ddd35b0d9cc2887566934
Author: Alex D <st...@apache.org>
AuthorDate: Thu Aug 17 05:54:52 2023 -0700

    SOLR-16536 Replace OpenTracing instrumentation with OpenTelemetry (#1841)
    
    OpenTracing libraries were completely removed and replaced with OpenTelemetry libraries
---
 solr/CHANGES.txt                                   |   2 +
 solr/core/build.gradle                             |   6 +-
 .../src/java/org/apache/solr/api/AnnotatedApi.java |   2 +-
 solr/core/src/java/org/apache/solr/api/Api.java    |   2 +-
 .../src/java/org/apache/solr/api/V2HttpCall.java   |  10 +-
 .../java/org/apache/solr/core/CoreContainer.java   |  10 +-
 .../org/apache/solr/core/TracerConfigurator.java   |  80 +-------
 .../handler/admin/api/RestoreCollectionAPI.java    |   2 +-
 .../solr/handler/component/HttpShardHandler.java   |  12 +-
 .../solr/handler/component/ShardRequest.java       |   5 -
 .../solr/request/DelegatingSolrQueryRequest.java   |   8 +-
 .../org/apache/solr/request/SolrQueryRequest.java  |  22 +--
 .../apache/solr/request/SolrQueryRequestBase.java  |  32 ----
 .../java/org/apache/solr/servlet/HttpSolrCall.java |  22 +--
 .../java/org/apache/solr/servlet/ServletUtils.java |  49 ++---
 .../apache/solr/servlet/SolrDispatchFilter.java    |  27 +--
 .../org/apache/solr/update/SolrCmdDistributor.java |  13 +-
 .../java/org/apache/solr/update/UpdateCommand.java |  16 +-
 .../solr/util/tracing/HttpServletCarrier.java      |  92 ----------
 ...tCarrier.java => HttpServletRequestGetter.java} |  35 ++--
 ...rRequestCarrier.java => SolrRequestSetter.java} |  25 +--
 .../org/apache/solr/util/tracing/TraceUtils.java   | 116 +++++++++++-
 .../admin/api/AddReplicaPropertyAPITest.java       |   4 +-
 .../handler/admin/api/CoreReplicationAPITest.java  |   4 +-
 .../solr/handler/admin/api/ListAliasesAPITest.java |   4 +-
 ...rier.java => TestHttpServletRequestGetter.java} |  50 ++---
 .../opentelemetry-opentracing-shim-1.29.0.jar.sha1 |   1 -
 .../opentelemetry-sdk-testing-1.29.0.jar.sha1      |   1 +
 solr/licenses/opentracing-api-0.33.0.jar.sha1      |   1 -
 solr/licenses/opentracing-api-LICENSE-ASL.txt      | 201 ---------------------
 solr/licenses/opentracing-api-NOTICE.txt           |   0
 solr/licenses/opentracing-mock-0.33.0.jar.sha1     |   1 -
 solr/licenses/opentracing-mock-LICENSE-ASL.txt     | 201 ---------------------
 solr/licenses/opentracing-mock-NOTICE.txt          |   0
 solr/licenses/opentracing-noop-0.33.0.jar.sha1     |   1 -
 solr/licenses/opentracing-noop-LICENSE-ASL.txt     | 201 ---------------------
 solr/licenses/opentracing-noop-NOTICE.txt          |   0
 solr/licenses/opentracing-util-0.33.0.jar.sha1     |   1 -
 solr/licenses/opentracing-util-LICENSE-ASL.txt     | 201 ---------------------
 solr/licenses/opentracing-util-NOTICE.txt          |   0
 solr/modules/opentelemetry/build.gradle            |  14 +-
 .../solr/opentelemetry/ClosableTracerShim.java     |  85 ---------
 .../solr/opentelemetry/OtelTracerConfigurator.java |  38 ++--
 .../opentelemetry/src/test-files/solr/solr.xml     |   2 +-
 .../CustomTestOtelTracerConfigurator.java          | 104 +++++++++++
 .../opentelemetry/OtelTracerConfiguratorTest.java  |  44 +----
 .../opentelemetry}/TestDistributedTracing.java     | 141 ++++++++-------
 .../pages/major-changes-in-solr-10.adoc            |   2 +
 solr/test-framework/build.gradle                   |   2 -
 .../src/java/org/apache/solr/SolrTestCaseJ4.java   |  36 +---
 versions.lock                                      |  12 +-
 versions.props                                     |   1 -
 52 files changed, 469 insertions(+), 1472 deletions(-)

diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index b82508e9e6b..098080ed7c2 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -17,6 +17,8 @@ Improvements
 
 * SOLR-16800: Solr CLI commands that use -solrUrl now work with a bare url like http://localhost:8983, you no longer need to add the /solr at the end. (Eric Pugh)
 
+* SOLR-16536: Replace OpenTracing instrumentation with OpenTelemetry (Alex Deparvu, janhoy)
+
 Optimizations
 ---------------------
 (No changes)
diff --git a/solr/core/build.gradle b/solr/core/build.gradle
index d868164cd75..61ecd1713af 100644
--- a/solr/core/build.gradle
+++ b/solr/core/build.gradle
@@ -141,10 +141,8 @@ dependencies {
   implementation 'com.tdunning:t-digest'
 
   // Distributed Tracing
-  api 'io.opentracing:opentracing-api' // Tracer is exposed on some methods
-  implementation 'io.opentracing:opentracing-noop'
-  implementation 'io.opentracing:opentracing-util'
-  testImplementation 'io.opentracing:opentracing-mock'
+  api 'io.opentelemetry:opentelemetry-api' // Tracer is exposed on some methods
+  implementation 'io.opentelemetry:opentelemetry-context'
 
   implementation 'org.apache.commons:commons-exec'
 
diff --git a/solr/core/src/java/org/apache/solr/api/AnnotatedApi.java b/solr/core/src/java/org/apache/solr/api/AnnotatedApi.java
index 7ec6ab6a919..bfe35c47108 100644
--- a/solr/core/src/java/org/apache/solr/api/AnnotatedApi.java
+++ b/solr/core/src/java/org/apache/solr/api/AnnotatedApi.java
@@ -215,7 +215,7 @@ public class AnnotatedApi extends Api implements PermissionNameProvider, Closeab
     }
 
     for (CommandOperation cmd : cmds) {
-      TraceUtils.ifNotNoop(req.getSpan(), (span) -> span.log("Command: " + cmd.name));
+      TraceUtils.ifNotNoop(req.getSpan(), (span) -> span.addEvent("Command: " + cmd.name));
       commands.get(cmd.name).invoke(req, rsp, cmd);
     }
 
diff --git a/solr/core/src/java/org/apache/solr/api/Api.java b/solr/core/src/java/org/apache/solr/api/Api.java
index 54010912ae2..8e881b7f74e 100644
--- a/solr/core/src/java/org/apache/solr/api/Api.java
+++ b/solr/core/src/java/org/apache/solr/api/Api.java
@@ -41,7 +41,7 @@ public abstract class Api implements SpecProvider {
     if (commandSchema == null) {
       synchronized (this) {
         if (commandSchema == null) {
-          ValidatingJsonMap commands = getSpec().getMap("commands", null);
+          ValidatingJsonMap commands = spec != null ? getSpec().getMap("commands", null) : null;
           commandSchema =
               commands != null ? Map.copyOf(ApiBag.getParsedSchema(commands)) : Map.of();
         }
diff --git a/solr/core/src/java/org/apache/solr/api/V2HttpCall.java b/solr/core/src/java/org/apache/solr/api/V2HttpCall.java
index 46c66e403df..92008542a60 100644
--- a/solr/core/src/java/org/apache/solr/api/V2HttpCall.java
+++ b/solr/core/src/java/org/apache/solr/api/V2HttpCall.java
@@ -23,8 +23,7 @@ import static org.apache.solr.servlet.SolrDispatchFilter.Action.ADMIN_OR_REMOTEQ
 import static org.apache.solr.servlet.SolrDispatchFilter.Action.PROCESS;
 import static org.apache.solr.servlet.SolrDispatchFilter.Action.REMOTEQUERY;
 
-import io.opentracing.Span;
-import io.opentracing.tag.Tags;
+import io.opentelemetry.api.trace.Span;
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
 import java.util.ArrayList;
@@ -65,6 +64,7 @@ import org.apache.solr.servlet.HttpSolrCall;
 import org.apache.solr.servlet.SolrDispatchFilter;
 import org.apache.solr.servlet.SolrRequestParsers;
 import org.apache.solr.servlet.cache.Method;
+import org.apache.solr.util.tracing.TraceUtils;
 import org.glassfish.jersey.server.ApplicationHandler;
 import org.glassfish.jersey.server.ContainerRequest;
 import org.slf4j.Logger;
@@ -499,9 +499,7 @@ public class V2HttpCall extends HttpSolrCall {
         coreOrColName = pathTemplateValues.get("core");
       }
     }
-    if (coreOrColName != null) {
-      span.setTag(Tags.DB_INSTANCE, coreOrColName);
-    }
+    TraceUtils.setDbInstance(span, coreOrColName);
 
     // Get the templatize-ed path, ex: "/c/{collection}"
     final String path = computeEndpointPath();
@@ -521,7 +519,7 @@ public class V2HttpCall extends HttpSolrCall {
       verb = req.getMethod().toLowerCase(Locale.ROOT);
     }
 
-    span.setOperationName(verb + ":" + path);
+    span.updateName(verb + ":" + path);
   }
 
   /** Example: /c/collection1/ and template map collection->collection1 produces /c/{collection}. */
diff --git a/solr/core/src/java/org/apache/solr/core/CoreContainer.java b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
index f223f41e36f..5606d93dc85 100644
--- a/solr/core/src/java/org/apache/solr/core/CoreContainer.java
+++ b/solr/core/src/java/org/apache/solr/core/CoreContainer.java
@@ -31,9 +31,7 @@ import static org.apache.solr.security.AuthenticationPlugin.AUTHENTICATION_PLUGI
 
 import com.github.benmanes.caffeine.cache.Interner;
 import com.google.common.annotations.VisibleForTesting;
-import io.opentracing.Tracer;
-import io.opentracing.noop.NoopTracer;
-import io.opentracing.noop.NoopTracerFactory;
+import io.opentelemetry.api.trace.Tracer;
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
 import java.nio.file.Path;
@@ -268,7 +266,7 @@ public class CoreContainer {
 
   protected volatile SolrMetricsContext solrMetricsContext;
 
-  protected volatile Tracer tracer = NoopTracerFactory.create();
+  protected volatile Tracer tracer;
 
   protected MetricsHandler metricsHandler;
 
@@ -695,7 +693,7 @@ public class CoreContainer {
     return metricsHandler;
   }
 
-  /** Never null but may implement {@link NoopTracer}. */
+  /** Never null */
   public Tracer getTracer() {
     return tracer;
   }
@@ -1375,8 +1373,6 @@ public class CoreContainer {
       org.apache.lucene.util.IOUtils.closeWhileHandlingException(packageLoader);
     }
     org.apache.lucene.util.IOUtils.closeWhileHandlingException(loader); // best effort
-
-    tracer.close();
   }
 
   public void cancelCoreRecoveries() {
diff --git a/solr/core/src/java/org/apache/solr/core/TracerConfigurator.java b/solr/core/src/java/org/apache/solr/core/TracerConfigurator.java
index ba36ed127ec..bc1798863f7 100644
--- a/solr/core/src/java/org/apache/solr/core/TracerConfigurator.java
+++ b/solr/core/src/java/org/apache/solr/core/TracerConfigurator.java
@@ -17,83 +17,23 @@
 
 package org.apache.solr.core;
 
-import io.opentracing.Scope;
-import io.opentracing.Span;
-import io.opentracing.Tracer;
-import io.opentracing.util.GlobalTracer;
-import java.lang.invoke.MethodHandles;
-import java.util.concurrent.atomic.AtomicReference;
-import org.apache.solr.common.util.ExecutorUtil;
+import io.opentelemetry.api.trace.Tracer;
 import org.apache.solr.util.plugin.NamedListInitializedPlugin;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.apache.solr.util.tracing.TraceUtils;
 
-/** Produces an OpenTracing {@link Tracer} from configuration. */
+/** Produces a {@link Tracer} from configuration. */
 public abstract class TracerConfigurator implements NamedListInitializedPlugin {
-  private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
-
-  public abstract Tracer getTracer();
 
   public static Tracer loadTracer(SolrResourceLoader loader, PluginInfo info) {
-    // In "normal" Solr operation, loadTracer is called once in Solr's lifetime.
-    // In test mode we run many test suites, sometimes multiple servers per test suite.
-    // It's important that the tracing config not change throughout a test suite because of the
-    //   static singleton pattern and assumptions based on this.
-
     if (info != null && info.isEnabled()) {
-      GlobalTracer.registerIfAbsent(
-          () -> {
-            TracerConfigurator configurator =
-                loader.newInstance(info.className, TracerConfigurator.class);
-            configurator.init(info.initArgs);
-            return configurator.getTracer();
-          });
-    }
-    if (GlobalTracer.isRegistered()) {
-      // ideally we would furthermore check that it's not a no-op impl either but
-      //  GlobalTracer.get() always returns a GlobalTracer implementing Tracer that delegates
-      //  to the real Tracer (that may or may not be a No-Op impl).
-      ExecutorUtil.addThreadLocalProvider(new TracerConfigurator.SpanThreadLocalProvider());
+      TracerConfigurator configurator =
+          loader.newInstance(info.className, TracerConfigurator.class);
+      configurator.init(info.initArgs);
+      return configurator.getTracer();
+    } else {
+      return TraceUtils.noop();
     }
-
-    return GlobalTracer.get();
   }
 
-  /**
-   * Propagate the active span across threads. New spans are not created, which means that we're
-   * possibly exposing a Span to a thread that may find that it has already finished, depending on
-   * how the instrumented thread pool is used. It's probably fine to create new spans related to a
-   * finished span? It's not okay to otherwise touch a finished span (e.g. to log or tag).
-   *
-   * <p>This strategy is also used by {@code
-   * brave.propagation.CurrentTraceContext#wrap(java.lang.Runnable)}.
-   */
-  private static class SpanThreadLocalProvider
-      implements ExecutorUtil.InheritableThreadLocalProvider {
-    private final Tracer tracer = GlobalTracer.get();
-
-    @Override
-    public void store(AtomicReference<Object> ctx) {
-      assert tracer == GlobalTracer.get() : "Tracer changed; not supported!";
-      ctx.set(tracer.scopeManager().activeSpan());
-    }
-
-    @Override
-    public void set(AtomicReference<Object> ctx) {
-      final Span span = (Span) ctx.get();
-      if (span != null) {
-        log.trace("Thread received span to do async work: {}", span);
-        final Scope scope = tracer.scopeManager().activate(span);
-        ctx.set(scope);
-      }
-    }
-
-    @Override
-    public void clean(AtomicReference<Object> ctx) {
-      Scope scope = (Scope) ctx.get();
-      if (scope != null) {
-        scope.close();
-      }
-    }
-  }
+  protected abstract Tracer getTracer();
 }
diff --git a/solr/core/src/java/org/apache/solr/handler/admin/api/RestoreCollectionAPI.java b/solr/core/src/java/org/apache/solr/handler/admin/api/RestoreCollectionAPI.java
index 18caed1933a..1809d83f6b9 100644
--- a/solr/core/src/java/org/apache/solr/handler/admin/api/RestoreCollectionAPI.java
+++ b/solr/core/src/java/org/apache/solr/handler/admin/api/RestoreCollectionAPI.java
@@ -108,8 +108,8 @@ public class RestoreCollectionAPI extends BackupAPIBase {
     }
 
     final String collectionName = requestBody.collection;
-    recordCollectionForLogAndTracing(collectionName, solrQueryRequest);
     SolrIdentifierValidator.validateCollectionName(collectionName);
+    recordCollectionForLogAndTracing(collectionName, solrQueryRequest);
     if (coreContainer.getAliases().hasAlias(collectionName)) {
       throw new SolrException(
           SolrException.ErrorCode.BAD_REQUEST,
diff --git a/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandler.java b/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandler.java
index 0ad76294335..17fb78a86c6 100644
--- a/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandler.java
+++ b/solr/core/src/java/org/apache/solr/handler/component/HttpShardHandler.java
@@ -16,9 +16,6 @@
  */
 package org.apache.solr.handler.component;
 
-import io.opentracing.Span;
-import io.opentracing.Tracer;
-import io.opentracing.propagation.Format;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -50,7 +47,7 @@ import org.apache.solr.core.CoreDescriptor;
 import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.request.SolrRequestInfo;
 import org.apache.solr.security.AllowListUrlChecker;
-import org.apache.solr.util.tracing.SolrRequestCarrier;
+import org.apache.solr.util.tracing.TraceUtils;
 
 @NotThreadSafe
 public class HttpShardHandler extends ShardHandler {
@@ -128,8 +125,6 @@ public class HttpShardHandler extends ShardHandler {
       final ShardRequest sreq, final String shard, final ModifiableSolrParams params) {
     // do this outside of the callable for thread safety reasons
     final List<String> urls = getURLs(shard);
-    final Tracer tracer = sreq.tracer; // not null
-    final Span span = tracer.activeSpan(); // probably not null?
 
     params.remove(CommonParams.WT); // use default (currently javabin)
     params.remove(CommonParams.VERSION);
@@ -171,10 +166,7 @@ public class HttpShardHandler extends ShardHandler {
 
               @Override
               public void onStart() {
-                if (span != null) {
-                  tracer.inject(
-                      span.context(), Format.Builtin.HTTP_HEADERS, new SolrRequestCarrier(req));
-                }
+                TraceUtils.injectContextIntoRequest(req);
                 SolrRequestInfo requestInfo = SolrRequestInfo.getRequestInfo();
                 if (requestInfo != null)
                   req.setUserPrincipal(requestInfo.getReq().getUserPrincipal());
diff --git a/solr/core/src/java/org/apache/solr/handler/component/ShardRequest.java b/solr/core/src/java/org/apache/solr/handler/component/ShardRequest.java
index 03b660ff9be..d7d7da04f22 100644
--- a/solr/core/src/java/org/apache/solr/handler/component/ShardRequest.java
+++ b/solr/core/src/java/org/apache/solr/handler/component/ShardRequest.java
@@ -16,8 +16,6 @@
  */
 package org.apache.solr.handler.component;
 
-import io.opentracing.Tracer;
-import io.opentracing.util.GlobalTracer;
 import java.util.ArrayList;
 import java.util.List;
 import org.apache.solr.common.params.ModifiableSolrParams;
@@ -58,9 +56,6 @@ public class ShardRequest {
   /** may be null */
   public String nodeName;
 
-  /** Not null but may implement {@link io.opentracing.noop.NoopTracer}. */
-  public final Tracer tracer = GlobalTracer.get();
-
   // TODO: one could store a list of numbers to correlate where returned docs
   // go in the top-level response rather than looking up by id...
   // this would work well if we ever transitioned to using internal ids and
diff --git a/solr/core/src/java/org/apache/solr/request/DelegatingSolrQueryRequest.java b/solr/core/src/java/org/apache/solr/request/DelegatingSolrQueryRequest.java
index 2c9c20d4f06..158f02cdff3 100644
--- a/solr/core/src/java/org/apache/solr/request/DelegatingSolrQueryRequest.java
+++ b/solr/core/src/java/org/apache/solr/request/DelegatingSolrQueryRequest.java
@@ -16,8 +16,7 @@
  */
 package org.apache.solr.request;
 
-import io.opentracing.Span;
-import io.opentracing.Tracer;
+import io.opentelemetry.api.trace.Span;
 import java.security.Principal;
 import java.util.List;
 import java.util.Map;
@@ -150,11 +149,6 @@ public class DelegatingSolrQueryRequest implements SolrQueryRequest {
     return delegate.getHttpSolrCall();
   }
 
-  @Override
-  public Tracer getTracer() {
-    return delegate.getTracer();
-  }
-
   @Override
   public Span getSpan() {
     return delegate.getSpan();
diff --git a/solr/core/src/java/org/apache/solr/request/SolrQueryRequest.java b/solr/core/src/java/org/apache/solr/request/SolrQueryRequest.java
index 613ae31d438..113aeff6440 100644
--- a/solr/core/src/java/org/apache/solr/request/SolrQueryRequest.java
+++ b/solr/core/src/java/org/apache/solr/request/SolrQueryRequest.java
@@ -16,10 +16,7 @@
  */
 package org.apache.solr.request;
 
-import io.opentracing.Span;
-import io.opentracing.Tracer;
-import io.opentracing.noop.NoopSpan;
-import io.opentracing.util.GlobalTracer;
+import io.opentelemetry.api.trace.Span;
 import java.security.Principal;
 import java.util.Collections;
 import java.util.List;
@@ -136,21 +133,16 @@ public interface SolrQueryRequest extends AutoCloseable {
     return null;
   }
 
-  /**
-   * Distributed tracing Tracer. Never null but might implement {@link
-   * io.opentracing.noop.NoopTracer}.
-   */
-  default Tracer getTracer() {
-    return GlobalTracer.get(); // default impl is only for some tests
-  }
-
   /**
    * The distributed tracing Span for the request itself; never null. This is useful for adding tags
-   * or updating the operation name of the request span. If you need the current span, which might
-   * not necessarily be the request span, do this instead: {@code tracer.activeSpan()}.
+   * or updating the operation name of the request span. Not null.
    */
   default Span getSpan() {
-    return NoopSpan.INSTANCE;
+    final HttpSolrCall call = getHttpSolrCall();
+    if (call != null) {
+      return call.getSpan();
+    }
+    return Span.getInvalid();
   }
 
   default CoreContainer getCoreContainer() {
diff --git a/solr/core/src/java/org/apache/solr/request/SolrQueryRequestBase.java b/solr/core/src/java/org/apache/solr/request/SolrQueryRequestBase.java
index f1034b00bec..363022c3979 100644
--- a/solr/core/src/java/org/apache/solr/request/SolrQueryRequestBase.java
+++ b/solr/core/src/java/org/apache/solr/request/SolrQueryRequestBase.java
@@ -16,10 +16,6 @@
  */
 package org.apache.solr.request;
 
-import io.opentracing.Span;
-import io.opentracing.Tracer;
-import io.opentracing.noop.NoopSpan;
-import io.opentracing.util.GlobalTracer;
 import java.io.Closeable;
 import java.security.Principal;
 import java.util.Collections;
@@ -38,7 +34,6 @@ import org.apache.solr.common.util.ValidatingJsonMap;
 import org.apache.solr.core.SolrCore;
 import org.apache.solr.schema.IndexSchema;
 import org.apache.solr.search.SolrIndexSearcher;
-import org.apache.solr.servlet.HttpSolrCall;
 import org.apache.solr.util.RTimerTree;
 import org.apache.solr.util.RefCounted;
 
@@ -145,33 +140,6 @@ public abstract class SolrQueryRequestBase implements SolrQueryRequest, Closeabl
     return schema;
   }
 
-  @Override
-  public Tracer getTracer() {
-    final HttpSolrCall call = getHttpSolrCall();
-    if (call != null) {
-      final Tracer tracer = (Tracer) call.getReq().getAttribute(Tracer.class.getName());
-      if (tracer != null) {
-        return tracer;
-      }
-    }
-    if (core != null) {
-      return core.getCoreContainer().getTracer();
-    }
-    return GlobalTracer.get(); // this way is not ideal (particularly in tests) but it's okay
-  }
-
-  @Override
-  public Span getSpan() {
-    final HttpSolrCall call = getHttpSolrCall();
-    if (call != null) {
-      final Span span = (Span) call.getReq().getAttribute(Span.class.getName());
-      if (span != null) {
-        return span;
-      }
-    }
-    return NoopSpan.INSTANCE;
-  }
-
   @Override
   public void updateSchemaToLatest() {
     schema = core.getLatestSchema();
diff --git a/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java b/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java
index dbee25ec32f..f1e5a25693c 100644
--- a/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java
+++ b/solr/core/src/java/org/apache/solr/servlet/HttpSolrCall.java
@@ -32,8 +32,7 @@ import static org.apache.solr.servlet.SolrDispatchFilter.Action.REMOTEQUERY;
 import static org.apache.solr.servlet.SolrDispatchFilter.Action.RETRY;
 import static org.apache.solr.servlet.SolrDispatchFilter.Action.RETURN;
 
-import io.opentracing.Span;
-import io.opentracing.tag.Tags;
+import io.opentelemetry.api.trace.Span;
 import java.io.EOFException;
 import java.io.IOException;
 import java.io.InputStream;
@@ -50,7 +49,6 @@ import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Objects;
 import java.util.Random;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
@@ -626,10 +624,14 @@ public class HttpSolrCall {
         "handleOrForwardRequest should not be invoked when serving v1 requests.");
   }
 
-  /** Get the span for this request. Not null. */
-  protected Span getSpan() {
-    // Span was put into the request by SolrDispatchFilter
-    return (Span) Objects.requireNonNull(req.getAttribute(Span.class.getName()));
+  /** Get the Span for this request. Not null. */
+  public Span getSpan() {
+    var s = TraceUtils.getSpan(req);
+    if (s != null) {
+      return s;
+    } else {
+      return Span.getInvalid();
+    }
   }
 
   // called after init().
@@ -639,9 +641,7 @@ public class HttpSolrCall {
     if (coreOrColName == null && getCore() != null) {
       coreOrColName = getCore().getName();
     }
-    if (coreOrColName != null) {
-      span.setTag(Tags.DB_INSTANCE, coreOrColName);
-    }
+    TraceUtils.setDbInstance(span, coreOrColName);
 
     // Set operation name.
     String path = getPath();
@@ -655,7 +655,7 @@ public class HttpSolrCall {
     }
     String verb =
         getQueryParams().get(CoreAdminParams.ACTION, req.getMethod()).toLowerCase(Locale.ROOT);
-    span.setOperationName(verb + ":" + path);
+    span.updateName(verb + ":" + path);
   }
 
   public boolean shouldAudit() {
diff --git a/solr/core/src/java/org/apache/solr/servlet/ServletUtils.java b/solr/core/src/java/org/apache/solr/servlet/ServletUtils.java
index a286078dafb..5297fe44401 100644
--- a/solr/core/src/java/org/apache/solr/servlet/ServletUtils.java
+++ b/solr/core/src/java/org/apache/solr/servlet/ServletUtils.java
@@ -17,12 +17,8 @@
 
 package org.apache.solr.servlet;
 
-import io.opentracing.Span;
-import io.opentracing.Tracer;
-import io.opentracing.noop.NoopSpan;
-import io.opentracing.noop.NoopTracer;
-import io.opentracing.propagation.Format;
-import io.opentracing.tag.Tags;
+import io.opentelemetry.api.trace.Span;
+import io.opentelemetry.context.Context;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -45,7 +41,7 @@ import org.apache.http.HttpHeaders;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.SolrException.ErrorCode;
 import org.apache.solr.logging.MDCLoggingContext;
-import org.apache.solr.util.tracing.HttpServletCarrier;
+import org.apache.solr.util.tracing.TraceUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -234,15 +230,14 @@ public abstract class ServletUtils {
   private static void traceHttpRequestExecution2(
       HttpServletRequest request, HttpServletResponse response, Runnable tracedExecution)
       throws ServletException, IOException {
-    Tracer tracer = getTracer(request);
-    Span span = buildSpan(tracer, request);
-
-    request.setAttribute(SolrDispatchFilter.ATTR_TRACING_SPAN, span);
-    try (var scope = tracer.scopeManager().activate(span)) {
+    Context context = TraceUtils.extractContext(request);
+    Span span = TraceUtils.startHttpRequestSpan(request, context);
 
+    try (var scope = context.with(span).makeCurrent()) {
       assert scope != null; // prevent javac warning about scope being unused
-      MDCLoggingContext.setTracerId(span.context().toTraceId()); // handles empty string
-
+      TraceUtils.setSpan(request, span);
+      TraceUtils.ifNotNoop(
+          span, s -> MDCLoggingContext.setTracerId(s.getSpanContext().getTraceId()));
       tracedExecution.run();
     } catch (ExceptionWhileTracing e) {
       if (e.e instanceof SolrAuthenticationException) {
@@ -261,31 +256,9 @@ public abstract class ServletUtils {
         throw new RuntimeException(e.e);
       }
     } finally {
-      span.setTag(Tags.HTTP_STATUS, response.getStatus());
-      span.finish();
-    }
-  }
-
-  private static Tracer getTracer(HttpServletRequest req) {
-    return (Tracer) req.getAttribute(SolrDispatchFilter.ATTR_TRACING_TRACER);
-  }
-
-  protected static Span buildSpan(Tracer tracer, HttpServletRequest request) {
-    if (tracer instanceof NoopTracer) {
-      return NoopSpan.INSTANCE;
-    }
-    Tracer.SpanBuilder spanBuilder =
-        tracer
-            .buildSpan("http.request") // will be changed later
-            .asChildOf(tracer.extract(Format.Builtin.HTTP_HEADERS, new HttpServletCarrier(request)))
-            .withTag(Tags.SPAN_KIND, Tags.SPAN_KIND_SERVER)
-            .withTag(Tags.HTTP_METHOD, request.getMethod())
-            .withTag(Tags.HTTP_URL, request.getRequestURL().toString());
-    if (request.getQueryString() != null) {
-      spanBuilder.withTag("http.params", request.getQueryString());
+      TraceUtils.setHttpStatus(span, response.getStatus());
+      span.end();
     }
-    spanBuilder.withTag(Tags.DB_TYPE, "solr");
-    return spanBuilder.start();
   }
 
   // we make sure we read the full client request so that the client does
diff --git a/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java b/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java
index cfd9836c4ed..7b9c2b7e90c 100644
--- a/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java
+++ b/solr/core/src/java/org/apache/solr/servlet/SolrDispatchFilter.java
@@ -16,16 +16,13 @@
  */
 package org.apache.solr.servlet;
 
-import static org.apache.solr.security.AuditEvent.EventType;
 import static org.apache.solr.servlet.ServletUtils.closeShield;
 import static org.apache.solr.servlet.ServletUtils.configExcludes;
 import static org.apache.solr.servlet.ServletUtils.excludedPath;
+import static org.apache.solr.util.tracing.TraceUtils.getSpan;
+import static org.apache.solr.util.tracing.TraceUtils.setTracer;
 
 import com.google.common.annotations.VisibleForTesting;
-import io.opentracing.Span;
-import io.opentracing.Tracer;
-import io.opentracing.tag.Tags;
-import io.opentracing.util.GlobalTracer;
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
 import java.util.List;
@@ -54,10 +51,12 @@ import org.apache.solr.logging.MDCLoggingContext;
 import org.apache.solr.logging.MDCSnapshot;
 import org.apache.solr.request.SolrRequestInfo;
 import org.apache.solr.security.AuditEvent;
+import org.apache.solr.security.AuditEvent.EventType;
 import org.apache.solr.security.AuthenticationPlugin;
 import org.apache.solr.security.PKIAuthenticationPlugin;
 import org.apache.solr.security.PublicKeyHandler;
 import org.apache.solr.servlet.CoreContainerProvider.ServiceHolder;
+import org.apache.solr.util.tracing.TraceUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -73,8 +72,6 @@ import org.slf4j.LoggerFactory;
 // things like CoreContainer can be requested. (or better yet injected)
 public class SolrDispatchFilter extends BaseSolrFilter implements PathExcluder {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
-  public static final String ATTR_TRACING_SPAN = Span.class.getName();
-  public static final String ATTR_TRACING_TRACER = Tracer.class.getName();
 
   // TODO: see if we can get rid of the holder here (Servlet spec actually guarantees
   // ContextListeners run before filter init, but JettySolrRunner that we use for tests is
@@ -206,8 +203,7 @@ public class SolrDispatchFilter extends BaseSolrFilter implements PathExcluder {
     if (excludedPath(excludePatterns, request, response, chain)) {
       return;
     }
-    Tracer t = getCores() == null ? GlobalTracer.get() : getCores().getTracer();
-    request.setAttribute(ATTR_TRACING_TRACER, t);
+    setTracer(request, getCores().getTracer());
     RateLimitManager rateLimitManager = coreService.getService().getRateLimitManager();
     try {
       ServletUtils.rateLimitRequest(
@@ -228,10 +224,6 @@ public class SolrDispatchFilter extends BaseSolrFilter implements PathExcluder {
     }
   }
 
-  private static Span getSpan(HttpServletRequest req) {
-    return (Span) req.getAttribute(ATTR_TRACING_SPAN);
-  }
-
   private void dispatch(
       FilterChain chain, HttpServletRequest request, HttpServletResponse response, boolean retry)
       throws IOException, ServletException, SolrAuthenticationException {
@@ -242,11 +234,12 @@ public class SolrDispatchFilter extends BaseSolrFilter implements PathExcluder {
       request = wrappedRequest.get();
     }
 
+    var span = getSpan(request);
     if (getCores().getAuthenticationPlugin() != null) {
       if (log.isDebugEnabled()) {
         log.debug("User principal: {}", request.getUserPrincipal());
       }
-      getSpan(request).setTag(Tags.DB_USER, String.valueOf(request.getUserPrincipal()));
+      TraceUtils.setUser(span, String.valueOf(request.getUserPrincipal()));
     }
 
     HttpSolrCall call = getHttpSolrCall(request, response, retry);
@@ -255,15 +248,15 @@ public class SolrDispatchFilter extends BaseSolrFilter implements PathExcluder {
       Action result = call.call();
       switch (result) {
         case PASSTHROUGH:
-          getSpan(request).log("SolrDispatchFilter PASSTHROUGH");
+          span.addEvent("SolrDispatchFilter PASSTHROUGH");
           chain.doFilter(request, response);
           break;
         case RETRY:
-          getSpan(request).log("SolrDispatchFilter RETRY");
+          span.addEvent("SolrDispatchFilter RETRY");
           doFilter(request, response, chain, true); // RECURSION
           break;
         case FORWARD:
-          getSpan(request).log("SolrDispatchFilter FORWARD");
+          span.addEvent("SolrDispatchFilter FORWARD");
           request.getRequestDispatcher(call.getPath()).forward(request, response);
           break;
         case ADMIN:
diff --git a/solr/core/src/java/org/apache/solr/update/SolrCmdDistributor.java b/solr/core/src/java/org/apache/solr/update/SolrCmdDistributor.java
index b7687d4eb1b..b40c79a166f 100644
--- a/solr/core/src/java/org/apache/solr/update/SolrCmdDistributor.java
+++ b/solr/core/src/java/org/apache/solr/update/SolrCmdDistributor.java
@@ -16,9 +16,6 @@
  */
 package org.apache.solr.update;
 
-import io.opentracing.Span;
-import io.opentracing.Tracer;
-import io.opentracing.propagation.Format;
 import java.io.Closeable;
 import java.io.IOException;
 import java.io.InputStream;
@@ -52,7 +49,7 @@ import org.apache.solr.request.SolrRequestInfo;
 import org.apache.solr.update.processor.DistributedUpdateProcessor;
 import org.apache.solr.update.processor.DistributedUpdateProcessor.LeaderRequestReplicationTracker;
 import org.apache.solr.update.processor.DistributedUpdateProcessor.RollupRequestReplicationTracker;
-import org.apache.solr.util.tracing.SolrRequestCarrier;
+import org.apache.solr.util.tracing.TraceUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -314,13 +311,7 @@ public class SolrCmdDistributor implements Closeable {
     if (SolrRequestInfo.getRequestInfo() != null) {
       req.uReq.setUserPrincipal(SolrRequestInfo.getRequestInfo().getReq().getUserPrincipal());
     }
-
-    Tracer tracer = req.cmd.getTracer();
-    Span parentSpan = tracer.activeSpan();
-    if (parentSpan != null) {
-      tracer.inject(
-          parentSpan.context(), Format.Builtin.HTTP_HEADERS, new SolrRequestCarrier(req.uReq));
-    }
+    TraceUtils.injectContextIntoRequest(req.uReq);
 
     if (req.synchronous) {
       blockAndDoRetries();
diff --git a/solr/core/src/java/org/apache/solr/update/UpdateCommand.java b/solr/core/src/java/org/apache/solr/update/UpdateCommand.java
index 9714d712dae..79bbda1f3d3 100644
--- a/solr/core/src/java/org/apache/solr/update/UpdateCommand.java
+++ b/solr/core/src/java/org/apache/solr/update/UpdateCommand.java
@@ -16,13 +16,11 @@
  */
 package org.apache.solr.update;
 
-import io.opentracing.Tracer;
-import io.opentracing.util.GlobalTracer;
 import org.apache.solr.request.SolrQueryRequest;
 
 /** An index update command encapsulated in an object (Command pattern) */
 public abstract class UpdateCommand implements Cloneable {
-  protected SolrQueryRequest req;
+  protected final SolrQueryRequest req;
   protected long version;
   protected String route;
   protected int flags;
@@ -91,18 +89,6 @@ public abstract class UpdateCommand implements Cloneable {
     return req;
   }
 
-  public void setReq(SolrQueryRequest req) {
-    this.req = req;
-  }
-
-  /**
-   * Distributed tracing Tracer. Never null but might implement {@link
-   * io.opentracing.noop.NoopTracer}.
-   */
-  public Tracer getTracer() {
-    return (req != null) ? req.getTracer() : GlobalTracer.get();
-  }
-
   @Override
   public UpdateCommand clone() {
     try {
diff --git a/solr/core/src/java/org/apache/solr/util/tracing/HttpServletCarrier.java b/solr/core/src/java/org/apache/solr/util/tracing/HttpServletCarrier.java
deleted file mode 100644
index 21ce05e29d1..00000000000
--- a/solr/core/src/java/org/apache/solr/util/tracing/HttpServletCarrier.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.solr.util.tracing;
-
-import io.opentracing.propagation.TextMap;
-import java.util.Enumeration;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.NoSuchElementException;
-import javax.servlet.http.HttpServletRequest;
-
-/** A Carrier for extract Span context out of request headers */
-public class HttpServletCarrier implements TextMap {
-  private Iterator<Map.Entry<String, String>> it;
-
-  public HttpServletCarrier(HttpServletRequest request) {
-    this.it =
-        new Iterator<>() {
-
-          Enumeration<String> headerNameIt = request.getHeaderNames();
-          String headerName = null;
-          Enumeration<String> headerValue = null;
-
-          @Override
-          public boolean hasNext() {
-            if (headerValue != null && headerValue.hasMoreElements()) {
-              return true;
-            }
-
-            return headerNameIt.hasMoreElements();
-          }
-
-          @Override
-          public Map.Entry<String, String> next() {
-            if (!hasNext()) {
-              throw new NoSuchElementException();
-            }
-
-            if (headerValue == null || !headerValue.hasMoreElements()) {
-              headerName = headerNameIt.nextElement();
-              headerValue = request.getHeaders(headerName);
-            }
-
-            String key = headerName;
-            String val = headerValue.nextElement();
-
-            return new Map.Entry<>() {
-              @Override
-              public String getKey() {
-                return key;
-              }
-
-              @Override
-              public String getValue() {
-                return val;
-              }
-
-              @Override
-              public String setValue(String value) {
-                throw new UnsupportedOperationException();
-              }
-            };
-          }
-        };
-  }
-
-  @Override
-  public Iterator<Map.Entry<String, String>> iterator() {
-    return it;
-  }
-
-  @Override
-  public void put(String key, String value) {
-    throw new UnsupportedOperationException(
-        "HttpServletCarrier should only be used with Tracer.extract()");
-  }
-}
diff --git a/solr/core/src/java/org/apache/solr/util/tracing/SolrRequestCarrier.java b/solr/core/src/java/org/apache/solr/util/tracing/HttpServletRequestGetter.java
similarity index 56%
copy from solr/core/src/java/org/apache/solr/util/tracing/SolrRequestCarrier.java
copy to solr/core/src/java/org/apache/solr/util/tracing/HttpServletRequestGetter.java
index 6f4917e1b8f..f2756566ed9 100644
--- a/solr/core/src/java/org/apache/solr/util/tracing/SolrRequestCarrier.java
+++ b/solr/core/src/java/org/apache/solr/util/tracing/HttpServletRequestGetter.java
@@ -14,30 +14,33 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.apache.solr.util.tracing;
 
-import io.opentracing.propagation.TextMap;
+import io.opentelemetry.context.propagation.TextMapGetter;
 import java.util.Iterator;
-import java.util.Map;
-import org.apache.solr.client.solrj.SolrRequest;
-
-/** An OpenTracing Carrier for injecting Span context through SolrRequest */
-public class SolrRequestCarrier implements TextMap {
-
-  private final SolrRequest<?> solrRequest;
+import javax.servlet.http.HttpServletRequest;
 
-  public SolrRequestCarrier(SolrRequest<?> solrRequest) {
-    this.solrRequest = solrRequest;
-  }
+/**
+ * {@code HttpServletRequest} aware {@code TextMapGetter} that allows header data to be extracted
+ * from a request
+ */
+public class HttpServletRequestGetter implements TextMapGetter<HttpServletRequest> {
 
   @Override
-  public Iterator<Map.Entry<String, String>> iterator() {
-    throw new UnsupportedOperationException("carrier is write-only");
+  public Iterable<String> keys(HttpServletRequest carrier) {
+    return new Iterable<String>() {
+      @Override
+      public Iterator<String> iterator() {
+        return carrier.getHeaderNames().asIterator();
+      }
+    };
   }
 
   @Override
-  public void put(String key, String value) {
-    solrRequest.addHeader(key, value);
+  public String get(HttpServletRequest carrier, String key) {
+    if (carrier == null) {
+      return null;
+    }
+    return carrier.getHeader(key);
   }
 }
diff --git a/solr/core/src/java/org/apache/solr/util/tracing/SolrRequestCarrier.java b/solr/core/src/java/org/apache/solr/util/tracing/SolrRequestSetter.java
similarity index 60%
rename from solr/core/src/java/org/apache/solr/util/tracing/SolrRequestCarrier.java
rename to solr/core/src/java/org/apache/solr/util/tracing/SolrRequestSetter.java
index 6f4917e1b8f..0923d9db014 100644
--- a/solr/core/src/java/org/apache/solr/util/tracing/SolrRequestCarrier.java
+++ b/solr/core/src/java/org/apache/solr/util/tracing/SolrRequestSetter.java
@@ -17,27 +17,16 @@
 
 package org.apache.solr.util.tracing;
 
-import io.opentracing.propagation.TextMap;
-import java.util.Iterator;
-import java.util.Map;
+import io.opentelemetry.context.propagation.TextMapSetter;
 import org.apache.solr.client.solrj.SolrRequest;
 
-/** An OpenTracing Carrier for injecting Span context through SolrRequest */
-public class SolrRequestCarrier implements TextMap {
-
-  private final SolrRequest<?> solrRequest;
-
-  public SolrRequestCarrier(SolrRequest<?> solrRequest) {
-    this.solrRequest = solrRequest;
-  }
-
-  @Override
-  public Iterator<Map.Entry<String, String>> iterator() {
-    throw new UnsupportedOperationException("carrier is write-only");
-  }
+/**
+ * {@code SolrRequest} aware {@code TextMapSetter} that allows header data to be added to a request
+ */
+public class SolrRequestSetter implements TextMapSetter<SolrRequest<?>> {
 
   @Override
-  public void put(String key, String value) {
-    solrRequest.addHeader(key, value);
+  public void set(SolrRequest<?> request, String key, String value) {
+    request.addHeader(key, value);
   }
 }
diff --git a/solr/core/src/java/org/apache/solr/util/tracing/TraceUtils.java b/solr/core/src/java/org/apache/solr/util/tracing/TraceUtils.java
index c7d2f0f0fd9..3505f4daac6 100644
--- a/solr/core/src/java/org/apache/solr/util/tracing/TraceUtils.java
+++ b/solr/core/src/java/org/apache/solr/util/tracing/TraceUtils.java
@@ -16,24 +16,128 @@
  */
 package org.apache.solr.util.tracing;
 
-import io.opentracing.Span;
-import io.opentracing.noop.NoopSpan;
-import io.opentracing.tag.Tags;
+import io.opentelemetry.api.GlobalOpenTelemetry;
+import io.opentelemetry.api.common.AttributeKey;
+import io.opentelemetry.api.trace.Span;
+import io.opentelemetry.api.trace.SpanBuilder;
+import io.opentelemetry.api.trace.SpanKind;
+import io.opentelemetry.api.trace.Tracer;
+import io.opentelemetry.api.trace.TracerProvider;
+import io.opentelemetry.context.Context;
+import io.opentelemetry.context.propagation.TextMapPropagator;
 import java.util.function.Consumer;
+import javax.servlet.http.HttpServletRequest;
+import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.request.SolrQueryRequest;
 
 /** Utilities for distributed tracing. */
 public class TraceUtils {
 
+  private static final String REQ_ATTR_TRACING_SPAN = Span.class.getName();
+  private static final String REQ_ATTR_TRACING_TRACER = Tracer.class.getName();
+  private static final Tracer NOOP_TRACER = TracerProvider.noop().get(null);
+
+  public static final String DEFAULT_SPAN_NAME = "http.request";
+  public static final String WRITE_QUERY_RESPONSE_SPAN_NAME = "writeQueryResponse";
+
+  public static final AttributeKey<String> TAG_DB = AttributeKey.stringKey("db.instance");
+  public static final AttributeKey<String> TAG_DB_TYPE = AttributeKey.stringKey("db.type");
+  public static final AttributeKey<String> TAG_USER = AttributeKey.stringKey("db.user");
+  public static final AttributeKey<Long> TAG_HTTP_STATUS =
+      AttributeKey.longKey("http.response.status_code");
+  public static final AttributeKey<String> TAG_HTTP_METHOD =
+      AttributeKey.stringKey("http.request.method");
+  public static final AttributeKey<String> TAG_HTTP_URL = AttributeKey.stringKey("http.url");
+  public static final AttributeKey<String> TAG_HTTP_PARAMS = AttributeKey.stringKey("http.params");
+  public static final AttributeKey<String> TAG_RESPONSE_WRITER =
+      AttributeKey.stringKey("responseWriter");
+  public static final AttributeKey<String> TAG_CONTENT_TYPE = AttributeKey.stringKey("contentType");
+
+  @Deprecated
+  private static final AttributeKey<String> TAG_HTTP_METHOD_DEP =
+      AttributeKey.stringKey("http.method");
+
+  @Deprecated
+  private static final AttributeKey<Long> TAG_HTTP_STATUS_DEP =
+      AttributeKey.longKey("http.status_code");
+
+  public static final String TAG_DB_TYPE_SOLR = "solr";
+
+  public static Tracer noop() {
+    return NOOP_TRACER;
+  }
+
+  public static TextMapPropagator getTextMapPropagator() {
+    return GlobalOpenTelemetry.getPropagators().getTextMapPropagator();
+  }
+
   public static void setDbInstance(SolrQueryRequest req, String coreOrColl) {
-    if (req != null && coreOrColl != null) {
-      ifNotNoop(req.getSpan(), (span) -> span.setTag(Tags.DB_INSTANCE, coreOrColl));
+    if (req != null) {
+      setDbInstance(req.getSpan(), coreOrColl);
+    }
+  }
+
+  public static void setDbInstance(Span span, String coreOrColName) {
+    if (coreOrColName != null) {
+      span.setAttribute(TAG_DB, coreOrColName);
     }
   }
 
+  public static void setUser(Span span, String user) {
+    span.setAttribute(TAG_USER, user);
+  }
+
+  public static void setHttpStatus(Span span, int httpStatus) {
+    span.setAttribute(TAG_HTTP_STATUS, httpStatus);
+    span.setAttribute(TAG_HTTP_STATUS_DEP, httpStatus);
+  }
+
   public static void ifNotNoop(Span span, Consumer<Span> consumer) {
-    if (span != null && !(span instanceof NoopSpan)) {
+    if (span.isRecording()) {
       consumer.accept(span);
     }
   }
+
+  public static void setSpan(HttpServletRequest req, Span span) {
+    req.setAttribute(REQ_ATTR_TRACING_SPAN, span);
+  }
+
+  public static Span getSpan(HttpServletRequest req) {
+    return (Span) req.getAttribute(REQ_ATTR_TRACING_SPAN);
+  }
+
+  public static void setTracer(HttpServletRequest req, Tracer t) {
+    req.setAttribute(REQ_ATTR_TRACING_TRACER, t);
+  }
+
+  public static Tracer getTracer(HttpServletRequest req) {
+    return (Tracer) req.getAttribute(REQ_ATTR_TRACING_TRACER);
+  }
+
+  public static Context extractContext(HttpServletRequest req) {
+    TextMapPropagator textMapPropagator = getTextMapPropagator();
+    return textMapPropagator.extract(Context.current(), req, new HttpServletRequestGetter());
+  }
+
+  public static void injectContextIntoRequest(SolrRequest<?> req) {
+    TextMapPropagator textMapPropagator = getTextMapPropagator();
+    textMapPropagator.inject(Context.current(), req, new SolrRequestSetter());
+  }
+
+  public static Span startHttpRequestSpan(HttpServletRequest request, Context context) {
+    Tracer tracer = getTracer(request);
+    SpanBuilder spanBuilder =
+        tracer
+            .spanBuilder(DEFAULT_SPAN_NAME) // will be changed later
+            .setParent(context)
+            .setSpanKind(SpanKind.SERVER)
+            .setAttribute(TAG_HTTP_METHOD, request.getMethod())
+            .setAttribute(TAG_HTTP_METHOD_DEP, request.getMethod())
+            .setAttribute(TAG_HTTP_URL, request.getRequestURL().toString());
+    if (request.getQueryString() != null) {
+      spanBuilder.setAttribute(TAG_HTTP_PARAMS, request.getQueryString());
+    }
+    spanBuilder.setAttribute(TAG_DB_TYPE, TAG_DB_TYPE_SOLR);
+    return spanBuilder.startSpan();
+  }
 }
diff --git a/solr/core/src/test/org/apache/solr/handler/admin/api/AddReplicaPropertyAPITest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/AddReplicaPropertyAPITest.java
index 501d22b130c..6de9c823536 100644
--- a/solr/core/src/test/org/apache/solr/handler/admin/api/AddReplicaPropertyAPITest.java
+++ b/solr/core/src/test/org/apache/solr/handler/admin/api/AddReplicaPropertyAPITest.java
@@ -24,7 +24,7 @@ import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import io.opentracing.noop.NoopSpan;
+import io.opentelemetry.api.trace.Span;
 import java.util.Map;
 import java.util.Optional;
 import org.apache.solr.SolrTestCaseJ4;
@@ -73,7 +73,7 @@ public class AddReplicaPropertyAPITest extends SolrTestCaseJ4 {
     when(mockCommandRunner.runCollectionCommand(any(), any(), anyLong()))
         .thenReturn(new OverseerSolrResponse(new NamedList<>()));
     mockQueryRequest = mock(SolrQueryRequest.class);
-    when(mockQueryRequest.getSpan()).thenReturn(NoopSpan.INSTANCE);
+    when(mockQueryRequest.getSpan()).thenReturn(Span.getInvalid());
     queryResponse = new SolrQueryResponse();
     messageCapturer = ArgumentCaptor.forClass(ZkNodeProps.class);
 
diff --git a/solr/core/src/test/org/apache/solr/handler/admin/api/CoreReplicationAPITest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/CoreReplicationAPITest.java
index 19fd1ea96e2..e6fe9edad0b 100644
--- a/solr/core/src/test/org/apache/solr/handler/admin/api/CoreReplicationAPITest.java
+++ b/solr/core/src/test/org/apache/solr/handler/admin/api/CoreReplicationAPITest.java
@@ -20,7 +20,7 @@ package org.apache.solr.handler.admin.api;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
-import io.opentracing.noop.NoopSpan;
+import io.opentelemetry.api.trace.Span;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -53,7 +53,7 @@ public class CoreReplicationAPITest extends SolrTestCaseJ4 {
     super.setUp();
     setUpMocks();
     mockQueryRequest = mock(SolrQueryRequest.class);
-    when(mockQueryRequest.getSpan()).thenReturn(NoopSpan.INSTANCE);
+    when(mockQueryRequest.getSpan()).thenReturn(Span.getInvalid());
     queryResponse = new SolrQueryResponse();
     coreReplicationAPI = new CoreReplicationAPIMock(mockCore, mockQueryRequest, queryResponse);
   }
diff --git a/solr/core/src/test/org/apache/solr/handler/admin/api/ListAliasesAPITest.java b/solr/core/src/test/org/apache/solr/handler/admin/api/ListAliasesAPITest.java
index 4cb4d065e36..2a6607c40b6 100644
--- a/solr/core/src/test/org/apache/solr/handler/admin/api/ListAliasesAPITest.java
+++ b/solr/core/src/test/org/apache/solr/handler/admin/api/ListAliasesAPITest.java
@@ -19,7 +19,7 @@ package org.apache.solr.handler.admin.api;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
-import io.opentracing.noop.NoopSpan;
+import io.opentelemetry.api.trace.Span;
 import java.util.List;
 import java.util.Map;
 import org.apache.solr.SolrTestCaseJ4;
@@ -67,7 +67,7 @@ public class ListAliasesAPITest extends SolrTestCaseJ4 {
     when(mockCoreContainer.getZkController()).thenReturn(zkController);
 
     mockQueryRequest = mock(SolrQueryRequest.class);
-    when(mockQueryRequest.getSpan()).thenReturn(NoopSpan.INSTANCE);
+    when(mockQueryRequest.getSpan()).thenReturn(Span.getInvalid());
     queryResponse = new SolrQueryResponse();
 
     getAliasesAPI = new ListAliasesAPI(mockCoreContainer, mockQueryRequest, queryResponse);
diff --git a/solr/core/src/test/org/apache/solr/util/tracing/TestHttpServletCarrier.java b/solr/core/src/test/org/apache/solr/util/tracing/TestHttpServletRequestGetter.java
similarity index 54%
rename from solr/core/src/test/org/apache/solr/util/tracing/TestHttpServletCarrier.java
rename to solr/core/src/test/org/apache/solr/util/tracing/TestHttpServletRequestGetter.java
index 4001af36b1c..d2b5fdf9bbb 100644
--- a/solr/core/src/test/org/apache/solr/util/tracing/TestHttpServletCarrier.java
+++ b/solr/core/src/test/org/apache/solr/util/tracing/TestHttpServletRequestGetter.java
@@ -17,10 +17,6 @@
 
 package org.apache.solr.util.tracing;
 
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.HashMap;
@@ -29,37 +25,43 @@ import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
 import javax.servlet.http.HttpServletRequest;
-import org.apache.solr.SolrTestCaseJ4;
+import javax.servlet.http.HttpServletRequestWrapper;
+import org.apache.solr.SolrTestCase;
+import org.eclipse.jetty.server.Request;
 import org.junit.Test;
-import org.mockito.stubbing.Answer;
 
-public class TestHttpServletCarrier extends SolrTestCaseJ4 {
+public class TestHttpServletRequestGetter extends SolrTestCase {
 
   @Test
   public void test() {
-    SolrTestCaseJ4.assumeWorkingMockito();
-    HttpServletRequest req = mock(HttpServletRequest.class);
     Map<String, Set<String>> headers =
         Map.of(
-            "a", Set.of("a", "b", "c"),
-            "b", Set.of("a", "b"),
-            "c", Set.of("a"));
+            "a", Set.of("0"),
+            "b", Set.of("1"),
+            "c", Set.of("2"));
+
+    HttpServletRequest req =
+        new HttpServletRequestWrapper(new Request(null, null)) {
+
+          @Override
+          public String getHeader(String name) {
+            return headers.get(name).iterator().next();
+          }
 
-    when(req.getHeaderNames()).thenReturn(Collections.enumeration(headers.keySet()));
-    when(req.getHeaders(anyString()))
-        .thenAnswer(
-            (Answer<Enumeration<String>>)
-                inv -> {
-                  String key = inv.getArgument(0);
-                  return Collections.enumeration(headers.get(key));
-                });
+          @Override
+          public Enumeration<String> getHeaderNames() {
+            return Collections.enumeration(headers.keySet());
+          }
+        };
 
-    HttpServletCarrier servletCarrier = new HttpServletCarrier(req);
-    Iterator<Map.Entry<String, String>> it = servletCarrier.iterator();
+    HttpServletRequestGetter httpServletRequestGetter = new HttpServletRequestGetter();
+    Iterator<String> it = httpServletRequestGetter.keys(req).iterator();
     Map<String, Set<String>> resultBack = new HashMap<>();
     while (it.hasNext()) {
-      Map.Entry<String, String> entry = it.next();
-      resultBack.computeIfAbsent(entry.getKey(), k -> new HashSet<>()).add(entry.getValue());
+      String key = it.next();
+      resultBack
+          .computeIfAbsent(key, k -> new HashSet<>())
+          .add(httpServletRequestGetter.get(req, key));
     }
     assertEquals(headers, resultBack);
   }
diff --git a/solr/licenses/opentelemetry-opentracing-shim-1.29.0.jar.sha1 b/solr/licenses/opentelemetry-opentracing-shim-1.29.0.jar.sha1
deleted file mode 100644
index 1d378f65bc4..00000000000
--- a/solr/licenses/opentelemetry-opentracing-shim-1.29.0.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-cbe64aea8ea3811911bf6e572f96ec0bc745948d
diff --git a/solr/licenses/opentelemetry-sdk-testing-1.29.0.jar.sha1 b/solr/licenses/opentelemetry-sdk-testing-1.29.0.jar.sha1
new file mode 100644
index 00000000000..87fa435be2c
--- /dev/null
+++ b/solr/licenses/opentelemetry-sdk-testing-1.29.0.jar.sha1
@@ -0,0 +1 @@
+3f36cd924e7631a6888d2280a3b368b3dce3acfc
diff --git a/solr/licenses/opentracing-api-0.33.0.jar.sha1 b/solr/licenses/opentracing-api-0.33.0.jar.sha1
deleted file mode 100644
index c7b2aafb365..00000000000
--- a/solr/licenses/opentracing-api-0.33.0.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-67336cfb9d93779c02e1fda4c87801d352720eda
diff --git a/solr/licenses/opentracing-api-LICENSE-ASL.txt b/solr/licenses/opentracing-api-LICENSE-ASL.txt
deleted file mode 100644
index 261eeb9e9f8..00000000000
--- a/solr/licenses/opentracing-api-LICENSE-ASL.txt
+++ /dev/null
@@ -1,201 +0,0 @@
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright [yyyy] [name of copyright owner]
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
diff --git a/solr/licenses/opentracing-api-NOTICE.txt b/solr/licenses/opentracing-api-NOTICE.txt
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/solr/licenses/opentracing-mock-0.33.0.jar.sha1 b/solr/licenses/opentracing-mock-0.33.0.jar.sha1
deleted file mode 100644
index 684a6c3d912..00000000000
--- a/solr/licenses/opentracing-mock-0.33.0.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-68f907743a5b355a8e975fdd3df0d2e106bbad6c
diff --git a/solr/licenses/opentracing-mock-LICENSE-ASL.txt b/solr/licenses/opentracing-mock-LICENSE-ASL.txt
deleted file mode 100644
index 261eeb9e9f8..00000000000
--- a/solr/licenses/opentracing-mock-LICENSE-ASL.txt
+++ /dev/null
@@ -1,201 +0,0 @@
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright [yyyy] [name of copyright owner]
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
diff --git a/solr/licenses/opentracing-mock-NOTICE.txt b/solr/licenses/opentracing-mock-NOTICE.txt
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/solr/licenses/opentracing-noop-0.33.0.jar.sha1 b/solr/licenses/opentracing-noop-0.33.0.jar.sha1
deleted file mode 100644
index ea4e70775a2..00000000000
--- a/solr/licenses/opentracing-noop-0.33.0.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-074b9950a587f53fbdb48c3f1f84f1ece8c10592
diff --git a/solr/licenses/opentracing-noop-LICENSE-ASL.txt b/solr/licenses/opentracing-noop-LICENSE-ASL.txt
deleted file mode 100644
index 261eeb9e9f8..00000000000
--- a/solr/licenses/opentracing-noop-LICENSE-ASL.txt
+++ /dev/null
@@ -1,201 +0,0 @@
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright [yyyy] [name of copyright owner]
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
diff --git a/solr/licenses/opentracing-noop-NOTICE.txt b/solr/licenses/opentracing-noop-NOTICE.txt
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/solr/licenses/opentracing-util-0.33.0.jar.sha1 b/solr/licenses/opentracing-util-0.33.0.jar.sha1
deleted file mode 100644
index b0c42dcc5bf..00000000000
--- a/solr/licenses/opentracing-util-0.33.0.jar.sha1
+++ /dev/null
@@ -1 +0,0 @@
-132630f17e198a1748f23ce33597efdf4a807fb9
diff --git a/solr/licenses/opentracing-util-LICENSE-ASL.txt b/solr/licenses/opentracing-util-LICENSE-ASL.txt
deleted file mode 100644
index 261eeb9e9f8..00000000000
--- a/solr/licenses/opentracing-util-LICENSE-ASL.txt
+++ /dev/null
@@ -1,201 +0,0 @@
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright [yyyy] [name of copyright owner]
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
diff --git a/solr/licenses/opentracing-util-NOTICE.txt b/solr/licenses/opentracing-util-NOTICE.txt
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/solr/modules/opentelemetry/build.gradle b/solr/modules/opentelemetry/build.gradle
index 444d3c6ffb4..4426f67649e 100644
--- a/solr/modules/opentelemetry/build.gradle
+++ b/solr/modules/opentelemetry/build.gradle
@@ -21,17 +21,13 @@ description = 'Open Telemetry (OTEL) tracer'
 
 dependencies {
   implementation project(':solr:core')
+  implementation project(':solr:solrj')
 
   implementation platform('io.opentelemetry:opentelemetry-bom')
-
-  implementation 'io.opentracing:opentracing-api'
   implementation 'org.slf4j:slf4j-api'
 
-  implementation 'io.opentelemetry:opentelemetry-sdk'
-  implementation 'io.opentelemetry:opentelemetry-sdk-trace'
+  implementation 'io.opentelemetry:opentelemetry-api'
   implementation 'io.opentelemetry:opentelemetry-sdk-extension-autoconfigure'
-  implementation 'io.opentelemetry:opentelemetry-opentracing-shim'
-  runtimeOnly 'io.opentelemetry:opentelemetry-semconv'
   runtimeOnly 'io.opentelemetry:opentelemetry-exporter-otlp'
   // End users must recompile with jaeger exporter and/or zipkin exporter if they need these
 
@@ -46,8 +42,8 @@ dependencies {
   compileOnly 'org.apache.tomcat:annotations-api'
 
   testImplementation project(':solr:test-framework')
-  testImplementation project(':solr:solrj')
-  testImplementation 'com.carrotsearch.randomizedtesting:randomizedtesting-runner'
   testImplementation 'junit:junit'
-  testImplementation 'io.opentracing:opentracing-util'
+  testImplementation 'io.opentelemetry:opentelemetry-sdk'
+  testImplementation 'io.opentelemetry:opentelemetry-sdk-trace'
+  testImplementation 'io.opentelemetry:opentelemetry-sdk-testing'
 }
diff --git a/solr/modules/opentelemetry/src/java/org/apache/solr/opentelemetry/ClosableTracerShim.java b/solr/modules/opentelemetry/src/java/org/apache/solr/opentelemetry/ClosableTracerShim.java
deleted file mode 100644
index ee7fb962ce9..00000000000
--- a/solr/modules/opentelemetry/src/java/org/apache/solr/opentelemetry/ClosableTracerShim.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.solr.opentelemetry;
-
-import io.opentelemetry.sdk.trace.SdkTracerProvider;
-import io.opentracing.Scope;
-import io.opentracing.ScopeManager;
-import io.opentracing.Span;
-import io.opentracing.SpanContext;
-import io.opentracing.Tracer;
-import io.opentracing.propagation.Format;
-import java.lang.invoke.MethodHandles;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Delegate shim that forwards all calls to the actual {@link
- * io.opentelemetry.opentracingshim.OpenTracingShim}, and in addition calls {@link
- * SdkTracerProvider#close()} to really close the OTEL SDK tracer when the OT shim is closed.
- *
- * <p>TODO: This can be removed once we migrate Solr instrumentation from OpenTracing to
- * OpenTelemetry
- */
-public class ClosableTracerShim implements Tracer {
-  private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
-  private final Tracer shim;
-  private final SdkTracerProvider sdkTracerProvider;
-
-  public ClosableTracerShim(Tracer shim, SdkTracerProvider sdkTracerProvider) {
-    this.shim = shim;
-    this.sdkTracerProvider = sdkTracerProvider;
-  }
-
-  @Override
-  public ScopeManager scopeManager() {
-    return shim.scopeManager();
-  }
-
-  @Override
-  public Span activeSpan() {
-    return shim.activeSpan();
-  }
-
-  @Override
-  public Scope activateSpan(Span span) {
-    return shim.activateSpan(span);
-  }
-
-  @Override
-  public SpanBuilder buildSpan(String operationName) {
-    return shim.buildSpan(operationName);
-  }
-
-  @Override
-  public <C> void inject(SpanContext spanContext, Format<C> format, C carrier) {
-    shim.inject(spanContext, format, carrier);
-  }
-
-  @Override
-  public <C> SpanContext extract(Format<C> format, C carrier) {
-    return shim.extract(format, carrier);
-  }
-
-  @Override
-  public void close() {
-    shim.close();
-    log.debug("Closing wrapped OTEL tracer instance.");
-    sdkTracerProvider.forceFlush();
-    sdkTracerProvider.close();
-  }
-}
diff --git a/solr/modules/opentelemetry/src/java/org/apache/solr/opentelemetry/OtelTracerConfigurator.java b/solr/modules/opentelemetry/src/java/org/apache/solr/opentelemetry/OtelTracerConfigurator.java
index 6b876c7c1b6..25d819718dc 100644
--- a/solr/modules/opentelemetry/src/java/org/apache/solr/opentelemetry/OtelTracerConfigurator.java
+++ b/solr/modules/opentelemetry/src/java/org/apache/solr/opentelemetry/OtelTracerConfigurator.java
@@ -16,10 +16,9 @@
  */
 package org.apache.solr.opentelemetry;
 
-import io.opentelemetry.opentracingshim.OpenTracingShim;
-import io.opentelemetry.sdk.OpenTelemetrySdk;
+import io.opentelemetry.api.GlobalOpenTelemetry;
+import io.opentelemetry.api.trace.Tracer;
 import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
-import io.opentracing.Tracer;
 import java.lang.invoke.MethodHandles;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -27,26 +26,45 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 import java.util.stream.Collectors;
+import org.apache.solr.common.util.NamedList;
 import org.apache.solr.core.TracerConfigurator;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * OpenTracing TracerConfigurator implementation which exports spans to OpenTelemetry in OTLP
- * format. This impl re-uses the existing OpenTracing instrumentation through a shim, and takes care
- * of properly closing the backing Tracer when Solr shuts down.
+ * Tracing TracerConfigurator implementation which exports spans to OpenTelemetry in OTLP format.
  */
 public class OtelTracerConfigurator extends TracerConfigurator {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
-  // Copy of environment. Can be overridden by tests
-  Map<String, String> currentEnv = System.getenv();
+
+  private final Map<String, String> currentEnv;
+
+  public OtelTracerConfigurator() {
+    this(System.getenv());
+  }
+
+  OtelTracerConfigurator(Map<String, String> currentEnv) {
+    this.currentEnv = currentEnv;
+  }
 
   @Override
   public Tracer getTracer() {
+    // TODO remove reliance on global
+    return GlobalOpenTelemetry.getTracer("solr");
+  }
+
+  @Override
+  public void init(NamedList<?> args) {
+    prepareConfiguration();
+    AutoConfiguredOpenTelemetrySdk.initialize();
+  }
+
+  void prepareConfiguration() {
     setDefaultIfNotConfigured("OTEL_SERVICE_NAME", "solr");
     setDefaultIfNotConfigured("OTEL_TRACES_EXPORTER", "otlp");
     setDefaultIfNotConfigured("OTEL_EXPORTER_OTLP_PROTOCOL", "grpc");
     setDefaultIfNotConfigured("OTEL_TRACES_SAMPLER", "parentbased_always_on");
+    setDefaultIfNotConfigured("OTEL_PROPAGATORS", "tracecontext,baggage");
     addOtelResourceAttributes(Map.of("host.name", System.getProperty("host")));
 
     final String currentConfig = getCurrentOtelConfigAsString();
@@ -62,10 +80,6 @@ public class OtelTracerConfigurator extends TracerConfigurator {
     }
     System.setProperty("otel.metrics.exporter", "none");
     System.setProperty("otel.logs.exporter", "none");
-
-    OpenTelemetrySdk otelSdk = AutoConfiguredOpenTelemetrySdk.initialize().getOpenTelemetrySdk();
-    Tracer shim = OpenTracingShim.createTracerShim(otelSdk);
-    return new ClosableTracerShim(shim, otelSdk.getSdkTracerProvider());
   }
 
   /**
diff --git a/solr/modules/opentelemetry/src/test-files/solr/solr.xml b/solr/modules/opentelemetry/src/test-files/solr/solr.xml
index 6ad63be5e84..76926369f0d 100644
--- a/solr/modules/opentelemetry/src/test-files/solr/solr.xml
+++ b/solr/modules/opentelemetry/src/test-files/solr/solr.xml
@@ -34,7 +34,7 @@
     <int name="connTimeout">${connTimeout:15000}</int>
   </shardHandlerFactory>
 
-  <tracerConfig name="tracerConfig" class="org.apache.solr.opentelemetry.OtelTracerConfigurator" />
+  <tracerConfig name="tracerConfig" class="org.apache.solr.opentelemetry.CustomTestOtelTracerConfigurator" />
 
   <solrcloud>
     <str name="host">127.0.0.1</str>
diff --git a/solr/modules/opentelemetry/src/test/org/apache/solr/opentelemetry/CustomTestOtelTracerConfigurator.java b/solr/modules/opentelemetry/src/test/org/apache/solr/opentelemetry/CustomTestOtelTracerConfigurator.java
new file mode 100644
index 00000000000..bad6cce7d9f
--- /dev/null
+++ b/solr/modules/opentelemetry/src/test/org/apache/solr/opentelemetry/CustomTestOtelTracerConfigurator.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.opentelemetry;
+
+import io.opentelemetry.api.GlobalOpenTelemetry;
+import io.opentelemetry.api.trace.Tracer;
+import io.opentelemetry.sdk.OpenTelemetrySdk;
+import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
+import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter;
+import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor;
+import java.lang.invoke.MethodHandles;
+import org.apache.solr.common.util.NamedList;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class CustomTestOtelTracerConfigurator extends OtelTracerConfigurator {
+
+  private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+  static {
+    if (System.getProperty("host") == null) {
+      System.setProperty("host", "localhost");
+    }
+  }
+
+  private static InMemorySpanExporter exporter;
+  private static volatile boolean isRegistered = false;
+  private static OpenTelemetrySdk otelSdk = null;
+
+  @Override
+  public synchronized Tracer getTracer() {
+    if (!isRegistered) {
+      throw new IllegalStateException(
+          "Tracer is not initialized. you need to call #prepareForTest for correct setup");
+    }
+    return super.getTracer();
+  }
+
+  @Override
+  public void init(NamedList<?> args) {
+    // prevent parent from init otel
+  }
+
+  public static synchronized void prepareForTest() {
+    CustomTestOtelTracerConfigurator.resetForTest();
+    isRegistered = true;
+    System.setProperty("otel.traces.exporter", "none");
+
+    // force early init
+    CustomTestOtelTracerConfigurator tracer = new CustomTestOtelTracerConfigurator();
+    tracer.prepareConfiguration();
+
+    bootOtel();
+  }
+
+  private static void bootOtel() {
+    try {
+      exporter = InMemorySpanExporter.create();
+      otelSdk =
+          AutoConfiguredOpenTelemetrySdk.builder()
+              .setResultAsGlobal()
+              .addTracerProviderCustomizer(
+                  (builder, props) ->
+                      builder.addSpanProcessor(SimpleSpanProcessor.create(exporter)))
+              .build()
+              .getOpenTelemetrySdk();
+    } catch (RuntimeException e) {
+      log.error("Error on OTEL init ", e);
+    }
+  }
+
+  public static InMemorySpanExporter getInMemorySpanExporter() {
+    return exporter;
+  }
+
+  public static synchronized void resetForTest() {
+    if (isRegistered) {
+      isRegistered = false;
+      if (otelSdk != null) {
+        otelSdk.close();
+      }
+      if (exporter != null) {
+        exporter.close();
+        exporter = null;
+      }
+      System.clearProperty("otel.traces.exporter");
+      GlobalOpenTelemetry.resetForTest();
+    }
+  }
+}
diff --git a/solr/modules/opentelemetry/src/test/org/apache/solr/opentelemetry/OtelTracerConfiguratorTest.java b/solr/modules/opentelemetry/src/test/org/apache/solr/opentelemetry/OtelTracerConfiguratorTest.java
index 5aef12103da..b4825c4e7fd 100644
--- a/solr/modules/opentelemetry/src/test/org/apache/solr/opentelemetry/OtelTracerConfiguratorTest.java
+++ b/solr/modules/opentelemetry/src/test/org/apache/solr/opentelemetry/OtelTracerConfiguratorTest.java
@@ -16,18 +16,13 @@
  */
 package org.apache.solr.opentelemetry;
 
-import com.carrotsearch.randomizedtesting.annotations.ThreadLeakLingering;
-import io.opentracing.util.GlobalTracer;
 import java.util.List;
 import java.util.Map;
 import org.apache.solr.SolrTestCaseJ4;
-import org.apache.solr.cloud.MiniSolrCloudCluster;
-import org.apache.solr.common.util.ExecutorUtil;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
-@ThreadLeakLingering(linger = 10000)
 public class OtelTracerConfiguratorTest extends SolrTestCaseJ4 {
   private OtelTracerConfigurator instance;
 
@@ -35,8 +30,7 @@ public class OtelTracerConfiguratorTest extends SolrTestCaseJ4 {
   @Before
   public void setUp() throws Exception {
     super.setUp();
-    instance = new OtelTracerConfigurator();
-    instance.currentEnv =
+    Map<String, String> currentEnv =
         Map.of(
             "OTELNOTHERE", "foo",
             "OTEL_K1", "env-k1",
@@ -44,10 +38,8 @@ public class OtelTracerConfiguratorTest extends SolrTestCaseJ4 {
     System.setProperty("otelnothere", "bar");
     System.setProperty("otel.k1", "prop-k1");
     System.setProperty("otel.k3", "prop-k3");
-
-    // to be safe because this test tests tracing.
-    resetGlobalTracer();
-    ExecutorUtil.resetThreadLocalProviders();
+    System.setProperty("host", "my.solr.host");
+    instance = new OtelTracerConfigurator(currentEnv);
   }
 
   @Override
@@ -57,7 +49,8 @@ public class OtelTracerConfiguratorTest extends SolrTestCaseJ4 {
     System.clearProperty("otelnothere");
     System.clearProperty("otel.k1");
     System.clearProperty("otel.k3");
-    System.clearProperty("otel.bsp.export.timeout");
+    System.clearProperty("host");
+    System.clearProperty("otel.resource.attributes");
   }
 
   @Test
@@ -93,28 +86,11 @@ public class OtelTracerConfiguratorTest extends SolrTestCaseJ4 {
   }
 
   @Test
-  public void testInjected() throws Exception {
+  public void testResourceAttributes() throws Exception {
     System.setProperty("otel.resource.attributes", "foo=bar,ILLEGAL-LACKS-VALUE,");
-    System.setProperty("host", "my.solr.host");
-    // Make sure the batch exporter times out before our thread lingering time of 10s
-    instance.setDefaultIfNotConfigured("OTEL_BSP_SCHEDULE_DELAY", "1000");
-    instance.setDefaultIfNotConfigured("OTEL_BSP_EXPORT_TIMEOUT", "2000");
-    MiniSolrCloudCluster cluster =
-        new MiniSolrCloudCluster.Builder(2, createTempDir())
-            .addConfig("config", TEST_PATH().resolve("collection1").resolve("conf"))
-            .withSolrXml(getFile("solr/solr.xml").toPath())
-            .build();
-    try {
-      assertTrue(
-          "Tracer shim not registered with GlobalTracer",
-          GlobalTracer.get().toString().contains("ClosableTracerShim"));
-      assertEquals(
-          List.of("host.name=my.solr.host", "foo=bar"),
-          List.of(System.getProperty("otel.resource.attributes").split(",")));
-    } finally {
-      cluster.shutdown();
-      System.clearProperty("otel.resource.attributes");
-      System.clearProperty("host");
-    }
+    instance.prepareConfiguration();
+    assertEquals(
+        List.of("host.name=my.solr.host", "foo=bar"),
+        List.of(System.getProperty("otel.resource.attributes").split(",")));
   }
 }
diff --git a/solr/core/src/test/org/apache/solr/util/tracing/TestDistributedTracing.java b/solr/modules/opentelemetry/src/test/org/apache/solr/opentelemetry/TestDistributedTracing.java
similarity index 56%
rename from solr/core/src/test/org/apache/solr/util/tracing/TestDistributedTracing.java
rename to solr/modules/opentelemetry/src/test/org/apache/solr/opentelemetry/TestDistributedTracing.java
index b28f15e18bd..dea832e87f6 100644
--- a/solr/core/src/test/org/apache/solr/util/tracing/TestDistributedTracing.java
+++ b/solr/modules/opentelemetry/src/test/org/apache/solr/opentelemetry/TestDistributedTracing.java
@@ -15,12 +15,15 @@
  * limitations under the License.
  */
 
-package org.apache.solr.util.tracing;
+package org.apache.solr.opentelemetry;
 
-import io.opentracing.mock.MockSpan;
-import io.opentracing.mock.MockTracer;
-import io.opentracing.util.GlobalTracer;
+import io.opentelemetry.api.GlobalOpenTelemetry;
+import io.opentelemetry.api.trace.SpanContext;
+import io.opentelemetry.api.trace.TracerProvider;
+import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter;
+import io.opentelemetry.sdk.trace.data.SpanData;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 import java.util.stream.Collectors;
@@ -34,56 +37,60 @@ import org.apache.solr.client.solrj.request.V2Request;
 import org.apache.solr.client.solrj.response.V2Response;
 import org.apache.solr.cloud.SolrCloudTestCase;
 import org.apache.solr.common.SolrDocumentList;
-import org.apache.solr.util.LogLevel;
-import org.hamcrest.MatcherAssert;
-import org.hamcrest.Matchers;
+import org.apache.solr.util.tracing.TraceUtils;
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
-@LogLevel("org.apache.solr.core.TracerConfigurator=trace")
 public class TestDistributedTracing extends SolrCloudTestCase {
-  private static final String COLLECTION = "collection1";
 
-  static MockTracer tracer;
+  private static final String COLLECTION = "collection1";
 
   @BeforeClass
-  public static void beforeTest() throws Exception {
-    tracer = new MockTracer();
-    assertTrue(GlobalTracer.registerIfAbsent(tracer));
+  public static void setupCluster() throws Exception {
+    // force early init
+    CustomTestOtelTracerConfigurator.prepareForTest();
 
     configureCluster(4)
-        .addConfig(
-            "config", TEST_PATH().resolve("configsets").resolve("cloud-minimal").resolve("conf"))
+        .addConfig("config", TEST_PATH().resolve("collection1").resolve("conf"))
+        .withSolrXml(TEST_PATH().resolve("solr.xml"))
         .configure();
+
+    assertNotEquals(
+        "Expecting active otel, not noop impl",
+        TracerProvider.noop(),
+        GlobalOpenTelemetry.get().getTracerProvider());
+
     CollectionAdminRequest.createCollection(COLLECTION, "config", 2, 2)
-        .setPerReplicaState(SolrCloudTestCase.USE_PER_REPLICA_STATE)
         .process(cluster.getSolrClient());
     cluster.waitForActiveCollection(COLLECTION, 2, 4);
   }
 
   @AfterClass
-  public static void afterTest() {
-    tracer = null;
+  public static void afterClass() {
+    CustomTestOtelTracerConfigurator.resetForTest();
   }
 
   @Test
   public void test() throws IOException, SolrServerException {
     // TODO it would be clearer if we could compare the complete Span tree between reality
-    //   and what we assert it looks like in a structured visual way.
+    // and what we assert it looks like in a structured visual way.
 
+    getAndClearSpans(); // reset
     CloudSolrClient cloudClient = cluster.getSolrClient();
-    List<MockSpan> finishedSpans;
 
     // Indexing
     cloudClient.add(COLLECTION, sdoc("id", "1"));
-    finishedSpans = getAndClearSpans();
-    finishedSpans.removeIf(x -> !x.tags().get("http.url").toString().endsWith("/update"));
+    var finishedSpans = getAndClearSpans();
+    finishedSpans.removeIf(
+        span ->
+            span.getAttributes().get(TraceUtils.TAG_HTTP_URL) == null
+                || !span.getAttributes().get(TraceUtils.TAG_HTTP_URL).endsWith("/update"));
     assertEquals(2, finishedSpans.size());
     assertOneSpanIsChildOfAnother(finishedSpans);
     // core because cloudClient routes to core
-    assertEquals("post:/{core}/update", finishedSpans.get(0).operationName());
-    assertDbInstanceCore(finishedSpans.get(0));
+    assertEquals("post:/{core}/update", finishedSpans.get(0).getName());
+    assertCoreName(finishedSpans.get(0));
 
     cloudClient.add(COLLECTION, sdoc("id", "2"));
     cloudClient.add(COLLECTION, sdoc("id", "3"));
@@ -94,54 +101,58 @@ public class TestDistributedTracing extends SolrCloudTestCase {
     // Searching
     cloudClient.query(COLLECTION, new SolrQuery("*:*"));
     finishedSpans = getAndClearSpans();
-    finishedSpans.removeIf(x -> !x.tags().get("http.url").toString().endsWith("/select"));
+    finishedSpans.removeIf(
+        span ->
+            span.getAttributes().get(TraceUtils.TAG_HTTP_URL) == null
+                || !span.getAttributes().get(TraceUtils.TAG_HTTP_URL).endsWith("/select"));
     // one from client to server, 2 for execute query, 2 for fetching documents
     assertEquals(5, finishedSpans.size());
-    assertEquals(1, finishedSpans.stream().filter(s -> s.parentId() == 0).count());
-    long parentId =
+    assertEquals(1, finishedSpans.stream().filter(TestDistributedTracing::isRootSpan).count());
+    var parentTraceId =
         finishedSpans.stream()
-            .filter(s -> s.parentId() == 0)
+            .filter(TestDistributedTracing::isRootSpan)
             .collect(Collectors.toList())
             .get(0)
-            .context()
-            .spanId();
-    for (MockSpan span : finishedSpans) {
-      if (span.parentId() != 0 && parentId != span.parentId()) {
-        fail("All spans must belong to single span, but:" + finishedSpans);
+            .getSpanContext()
+            .getTraceId();
+    for (var span : finishedSpans) {
+      if (isRootSpan(span)) {
+        continue;
       }
+      assertEquals(span.getParentSpanContext().getTraceId(), parentTraceId);
+      assertEquals(span.getTraceId(), parentTraceId);
     }
-    assertEquals("get:/{core}/select", finishedSpans.get(0).operationName());
-    assertDbInstanceCore(finishedSpans.get(0));
+    assertEquals("get:/{core}/select", finishedSpans.get(0).getName());
+    assertCoreName(finishedSpans.get(0));
   }
 
   @Test
   public void testAdminApi() throws Exception {
+    getAndClearSpans(); // reset
     CloudSolrClient cloudClient = cluster.getSolrClient();
-    List<MockSpan> finishedSpans;
 
-    // Admin API call
     cloudClient.request(new GenericSolrRequest(SolrRequest.METHOD.GET, "/admin/metrics"));
-    finishedSpans = getAndClearSpans();
-    assertEquals("get:/admin/metrics", finishedSpans.get(0).operationName());
+    var finishedSpans = getAndClearSpans();
+    assertEquals("get:/admin/metrics", finishedSpans.get(0).getName());
 
     CollectionAdminRequest.listCollections(cloudClient);
     finishedSpans = getAndClearSpans();
-    assertEquals("list:/admin/collections", finishedSpans.get(0).operationName());
+    assertEquals("list:/admin/collections", finishedSpans.get(0).getName());
   }
 
   @Test
   public void testV2Api() throws Exception {
+    getAndClearSpans(); // reset
     CloudSolrClient cloudClient = cluster.getSolrClient();
-    List<MockSpan> finishedSpans;
 
     new V2Request.Builder("/collections/" + COLLECTION + "/reload")
         .withMethod(SolrRequest.METHOD.POST)
         .withPayload("{}")
         .build()
         .process(cloudClient);
-    finishedSpans = getAndClearSpans();
-    assertEquals("post:/collections/{collection}/reload", finishedSpans.get(0).operationName());
-    assertDbInstanceColl(finishedSpans.get(0));
+    var finishedSpans = getAndClearSpans();
+    assertEquals("post:/collections/{collection}/reload", finishedSpans.get(0).getName());
+    assertCollectionName(finishedSpans.get(0));
 
     new V2Request.Builder("/c/" + COLLECTION + "/update/json")
         .withMethod(SolrRequest.METHOD.POST)
@@ -150,8 +161,8 @@ public class TestDistributedTracing extends SolrCloudTestCase {
         .build()
         .process(cloudClient);
     finishedSpans = getAndClearSpans();
-    assertEquals("post:/c/{collection}/update/json", finishedSpans.get(0).operationName());
-    assertDbInstanceColl(finishedSpans.get(0));
+    assertEquals("post:/c/{collection}/update/json", finishedSpans.get(0).getName());
+    assertCollectionName(finishedSpans.get(0));
 
     final V2Response v2Response =
         new V2Request.Builder("/c/" + COLLECTION + "/select")
@@ -160,36 +171,40 @@ public class TestDistributedTracing extends SolrCloudTestCase {
             .build()
             .process(cloudClient);
     finishedSpans = getAndClearSpans();
-    assertEquals("get:/c/{collection}/select", finishedSpans.get(0).operationName());
-    assertDbInstanceColl(finishedSpans.get(0));
+    assertEquals("get:/c/{collection}/select", finishedSpans.get(0).getName());
+    assertCollectionName(finishedSpans.get(0));
     assertEquals(1, ((SolrDocumentList) v2Response.getResponse().get("response")).getNumFound());
   }
 
-  private void assertDbInstanceColl(MockSpan mockSpan) {
-    MatcherAssert.assertThat(mockSpan.tags().get("db.instance"), Matchers.equalTo("collection1"));
+  private static boolean isRootSpan(SpanData span) {
+    return span.getParentSpanContext() == SpanContext.getInvalid();
   }
 
-  private void assertDbInstanceCore(MockSpan mockSpan) {
-    MatcherAssert.assertThat(
-        (String) mockSpan.tags().get("db.instance"), Matchers.startsWith("collection1_"));
+  private static void assertCollectionName(SpanData span) {
+    assertEquals(COLLECTION, span.getAttributes().get(TraceUtils.TAG_DB));
   }
 
-  private void assertOneSpanIsChildOfAnother(List<MockSpan> finishedSpans) {
-    MockSpan child = finishedSpans.get(0);
-    MockSpan parent = finishedSpans.get(1);
-    if (child.parentId() == 0) {
-      MockSpan temp = parent;
+  private static void assertCoreName(SpanData span) {
+    assertTrue(span.getAttributes().get(TraceUtils.TAG_DB).startsWith(COLLECTION + "_"));
+  }
+
+  private void assertOneSpanIsChildOfAnother(List<SpanData> finishedSpans) {
+    SpanData child = finishedSpans.get(0);
+    SpanData parent = finishedSpans.get(1);
+    if (isRootSpan(child)) {
+      var temp = parent;
       parent = child;
       child = temp;
     }
-
-    assertEquals(child.parentId(), parent.context().spanId());
+    assertEquals(child.getParentSpanContext().getTraceId(), parent.getTraceId());
+    assertEquals(child.getTraceId(), parent.getTraceId());
   }
 
-  private List<MockSpan> getAndClearSpans() {
-    List<MockSpan> result = tracer.finishedSpans(); // returns a mutable copy
+  private List<SpanData> getAndClearSpans() {
+    InMemorySpanExporter exporter = CustomTestOtelTracerConfigurator.getInMemorySpanExporter();
+    List<SpanData> result = new ArrayList<>(exporter.getFinishedSpanItems());
     Collections.reverse(result); // nicer to see spans chronologically
-    tracer.reset();
+    exporter.reset();
     return result;
   }
 }
diff --git a/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-10.adoc b/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-10.adoc
index 33e386b6b46..fed3ed14110 100644
--- a/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-10.adoc
+++ b/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-10.adoc
@@ -34,6 +34,8 @@ Starting in 10, the Maven POM for SolrJ does not refer to SolrJ modules like Zoo
 
 * The `jaegertracer-configurator` module, which was deprecated in 9.2, is removed. Users should migrate to the `opentelemetry` module.
 
+* `OpenTracing` libraries were removed and replaced with `OpenTelemetry` libraries. Any Java agents providing `OpenTracing` tracers will no longer work. Telemetry tags `http.status_code` and `http.method` have been deprecated, newer version of the tags have been added to the span data: `http.response.status_code`, `http.request.method`.
+
 * The `analytics` module, which was deprecated in 9.2, is removed.
 
 * The sysProp `-Dsolr.redaction.system.pattern`, which allows users to provide a pattern to match sysProps that should be redacted for sensitive information,
diff --git a/solr/test-framework/build.gradle b/solr/test-framework/build.gradle
index c6589155072..6c772aac512 100644
--- a/solr/test-framework/build.gradle
+++ b/solr/test-framework/build.gradle
@@ -52,8 +52,6 @@ dependencies {
   implementation 'org.slf4j:slf4j-api'
   implementation 'org.apache.logging.log4j:log4j-api'
   implementation 'org.apache.logging.log4j:log4j-core'
-  implementation 'io.opentracing:opentracing-noop'
-  implementation 'io.opentracing:opentracing-util'
   implementation 'io.dropwizard.metrics:metrics-core'
   implementation 'io.dropwizard.metrics:metrics-jetty10'
   implementation 'commons-cli:commons-cli'
diff --git a/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java b/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java
index ba96e729562..fc4a6ac27fb 100644
--- a/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java
+++ b/solr/test-framework/src/java/org/apache/solr/SolrTestCaseJ4.java
@@ -20,15 +20,12 @@ import static java.util.Objects.requireNonNull;
 import static org.apache.solr.cloud.SolrZkServer.ZK_WHITELIST_PROPERTY;
 import static org.apache.solr.common.cloud.ZkStateReader.HTTPS;
 import static org.apache.solr.common.cloud.ZkStateReader.URL_SCHEME;
-import static org.apache.solr.update.processor.DistributedUpdateProcessor.DistribPhase;
 import static org.apache.solr.update.processor.DistributingUpdateProcessorFactory.DISTRIB_UPDATE_PARAM;
 import static org.hamcrest.core.StringContains.containsString;
 
 import com.carrotsearch.randomizedtesting.RandomizedContext;
 import com.carrotsearch.randomizedtesting.RandomizedTest;
 import com.carrotsearch.randomizedtesting.rules.SystemPropertiesRestoreRule;
-import io.opentracing.noop.NoopTracerFactory;
-import io.opentracing.util.GlobalTracer;
 import java.io.File;
 import java.io.IOException;
 import java.io.OutputStreamWriter;
@@ -43,7 +40,6 @@ import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 import java.lang.invoke.MethodHandles;
-import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.net.URL;
@@ -52,8 +48,6 @@ import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.nio.file.StandardCopyOption;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
 import java.security.SecureRandom;
 import java.time.Instant;
 import java.util.ArrayList;
@@ -138,6 +132,7 @@ import org.apache.solr.search.SolrIndexSearcher;
 import org.apache.solr.security.AllowListUrlChecker;
 import org.apache.solr.servlet.DirectSolrConnection;
 import org.apache.solr.update.processor.DistributedUpdateProcessor;
+import org.apache.solr.update.processor.DistributedUpdateProcessor.DistribPhase;
 import org.apache.solr.update.processor.DistributedZkUpdateProcessor;
 import org.apache.solr.update.processor.UpdateRequestProcessor;
 import org.apache.solr.util.BaseTestHarness;
@@ -313,38 +308,9 @@ public abstract class SolrTestCaseJ4 extends SolrTestCase {
       System.setProperty(URL_SCHEME, HTTPS);
     }
 
-    resetGlobalTracer();
     ExecutorUtil.resetThreadLocalProviders();
   }
 
-  /**
-   * GlobalTracer is initialized by org.apache.solr.core.TracerConfigurator by
-   * org.apache.solr.core.CoreContainer. Tests may need to reset it in the beginning of a test if it
-   * might have differing configuration from other tests in the same suite. It's also important to
-   * call {@link ExecutorUtil#resetThreadLocalProviders()}.
-   */
-  @SuppressForbidden(reason = "Hack to reset internal state of GlobalTracer")
-  public static void resetGlobalTracer() {
-    AccessController.doPrivileged(
-        (PrivilegedAction<Void>)
-            () -> {
-              try {
-                final Class<GlobalTracer> globalTracerClass = GlobalTracer.class;
-                final Field isRegistered = globalTracerClass.getDeclaredField("isRegistered");
-                isRegistered.setAccessible(true);
-                isRegistered.setBoolean(null, false);
-                final Field tracer = globalTracerClass.getDeclaredField("tracer");
-                tracer.setAccessible(true);
-                tracer.set(null, NoopTracerFactory.create());
-              } catch (NoSuchFieldException | IllegalAccessException e) {
-                throw new RuntimeException(e);
-              }
-              return null;
-            });
-
-    assert GlobalTracer.isRegistered() == false;
-  }
-
   @AfterClass
   public static void teardownTestCases() throws Exception {
     TestInjection.notifyPauseForeverDone();
diff --git a/versions.lock b/versions.lock
index c78c403b7d4..f79f9dba576 100644
--- a/versions.lock
+++ b/versions.lock
@@ -124,7 +124,7 @@ io.netty:netty-transport-native-unix-common:4.1.96.Final (4 constraints: 5e3d199
 io.opencensus:opencensus-api:0.31.1 (5 constraints: 924d4692)
 io.opencensus:opencensus-contrib-http-util:0.31.1 (3 constraints: 7232a9fc)
 io.opencensus:opencensus-proto:0.2.0 (1 constraints: e60fd595)
-io.opentelemetry:opentelemetry-api:1.29.0 (11 constraints: ceca6768)
+io.opentelemetry:opentelemetry-api:1.29.0 (11 constraints: c3c8daff)
 io.opentelemetry:opentelemetry-api-events:1.29.0-alpha (2 constraints: 332f2380)
 io.opentelemetry:opentelemetry-bom:1.29.0 (1 constraints: 3e05483b)
 io.opentelemetry:opentelemetry-context:1.29.0 (2 constraints: 2d1f62b5)
@@ -133,18 +133,14 @@ io.opentelemetry:opentelemetry-exporter-otlp:1.29.0 (1 constraints: 990ff983)
 io.opentelemetry:opentelemetry-exporter-otlp-common:1.29.0 (2 constraints: 5823eb1c)
 io.opentelemetry:opentelemetry-exporter-sender-okhttp:1.29.0 (2 constraints: 5823eb1c)
 io.opentelemetry:opentelemetry-extension-incubator:1.29.0-alpha (1 constraints: f414ee96)
-io.opentelemetry:opentelemetry-opentracing-shim:1.29.0 (1 constraints: 990ff983)
-io.opentelemetry:opentelemetry-sdk:1.29.0 (3 constraints: ae4376e7)
+io.opentelemetry:opentelemetry-sdk:1.29.0 (4 constraints: 755634c7)
 io.opentelemetry:opentelemetry-sdk-common:1.29.0 (6 constraints: 936c396e)
 io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:1.29.0 (1 constraints: 990ff983)
 io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.29.0 (3 constraints: a63c5427)
 io.opentelemetry:opentelemetry-sdk-logs:1.29.0 (3 constraints: f43275b4)
 io.opentelemetry:opentelemetry-sdk-metrics:1.29.0 (3 constraints: f43275b4)
 io.opentelemetry:opentelemetry-sdk-trace:1.29.0 (3 constraints: f43275b4)
-io.opentelemetry:opentelemetry-semconv:1.29.0-alpha (4 constraints: 175b2583)
-io.opentracing:opentracing-api:0.33.0 (5 constraints: 7244331d)
-io.opentracing:opentracing-noop:0.33.0 (4 constraints: 48361e21)
-io.opentracing:opentracing-util:0.33.0 (2 constraints: 5013be5a)
+io.opentelemetry:opentelemetry-semconv:1.29.0-alpha (4 constraints: 0c5920ad)
 io.perfmark:perfmark-api:0.26.0 (3 constraints: 21212b16)
 io.prometheus:simpleclient:0.16.0 (3 constraints: 9d257513)
 io.prometheus:simpleclient_common:0.16.0 (1 constraints: 1a1139c0)
@@ -403,7 +399,7 @@ com.squareup.okhttp3:mockwebserver:4.9.3 (1 constraints: c60ebf62)
 io.github.microutils:kotlin-logging:2.1.21 (1 constraints: ec0e8871)
 io.github.microutils:kotlin-logging-jvm:2.1.21 (1 constraints: af0f358c)
 io.micrometer:micrometer-core:1.9.11 (1 constraints: fd162819)
-io.opentracing:opentracing-mock:0.33.0 (1 constraints: 3805343b)
+io.opentelemetry:opentelemetry-sdk-testing:1.29.0 (1 constraints: 990ff983)
 jakarta.servlet:jakarta.servlet-api:4.0.4 (4 constraints: 586e1f6a)
 jakarta.websocket:jakarta.websocket-api:1.1.2 (1 constraints: 92155ab9)
 javax.inject:javax.inject:1 (1 constraints: 7a0df617)
diff --git a/versions.props b/versions.props
index 45ffbbad5d4..41be40abeeb 100644
--- a/versions.props
+++ b/versions.props
@@ -24,7 +24,6 @@ io.dropwizard.metrics:*=4.2.19
 io.grpc:grpc-*=1.56.0
 io.netty:*=4.1.96.Final
 io.opentelemetry:opentelemetry-bom=1.29.0
-io.opentracing:*=0.33.0
 io.prometheus:*=0.16.0
 io.swagger.core.v3:*=2.2.12
 jakarta.ws.rs:jakarta.ws.rs-api=2.1.6