ctubbsii commented on a change in pull request #2259:
URL: https://github.com/apache/accumulo/pull/2259#discussion_r745113391



##########
File path: core/pom.xml
##########
@@ -262,6 +270,7 @@
               </includes>
               <excludes />
               <allows>
+                <allow>io[.]opentelemetry[.]api[.]OpenTelemetry</allow>

Review comment:
       This would allow using OpenTelemetry objects in Accumulo's own public 
API. This is needed because we have the OpenTelemetryFactory SPI. However, I'm 
second guessing the need for that, as it is generally undesirable to expose 
3rd-party types in our own public API, if we can help it.
   
   As an alternative, consider that OpenTelemetryFactory is nothing more than 
`Supplier<OpenTelemetry>`. As such, instead of having our own dedicated SPI 
type, we can just document that the implementing class be of type 
`Supplier<OpenTelemetry>`. I'm not sure this is the *best* idea, but it would 
have the advantage of keeping new 3rd-party types out of our API/SPI.
   
   Given the complexity of this, I'm also starting to wonder if we shouldn't 
just restrict the OpenTelemetry instance to whatever is set in the 
GlobalOpenTelemetry class, and just rely on users to use a JavaAgent or 
something to set it if they really want to, rather than even be given the 
option of configuring it via our code.

##########
File path: assemble/conf/accumulo-env.sh
##########
@@ -92,10 +92,23 @@ JAVA_OPTS=("${JAVA_OPTS[@]}"
   "-Daccumulo.application=${cmd}${ACCUMULO_SERVICE_INSTANCE}_$(hostname)"
   "-Daccumulo.metrics.service.instance=${ACCUMULO_SERVICE_INSTANCE}"
   
"-Dlog4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector"
+  "-Dotel.service.name=${cmd}${ACCUMULO_SERVICE_INSTANCE}"
 )
 
+## Optionally setup OpenTelemetry SDK AutoConfigure
+## See 
https://github.com/open-telemetry/opentelemetry-java/tree/main/sdk-extensions/autoconfigure
+#JAVA_OPTS=("${JAVA_OPTS[@]}"
+#  "-Dotel.traces.exporter=jaeger"
+#)

Review comment:
       This can take up less space
   ```suggestion
   #JAVA_OPTS=("${JAVA_OPTS[@]}" "-Dotel.traces.exporter=jaeger")
   ```

##########
File path: assemble/conf/accumulo-env.sh
##########
@@ -92,10 +92,23 @@ JAVA_OPTS=("${JAVA_OPTS[@]}"
   "-Daccumulo.application=${cmd}${ACCUMULO_SERVICE_INSTANCE}_$(hostname)"
   "-Daccumulo.metrics.service.instance=${ACCUMULO_SERVICE_INSTANCE}"
   
"-Dlog4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector"
+  "-Dotel.service.name=${cmd}${ACCUMULO_SERVICE_INSTANCE}"
 )
 
+## Optionally setup OpenTelemetry SDK AutoConfigure
+## See 
https://github.com/open-telemetry/opentelemetry-java/tree/main/sdk-extensions/autoconfigure
+#JAVA_OPTS=("${JAVA_OPTS[@]}"
+#  "-Dotel.traces.exporter=jaeger"
+#)
+
+## Optionally setup OpenTelemetry Java Agent
+## See https://github.com/open-telemetry/opentelemetry-java-instrumentation 
for more options
+#JAVA_OPTS=("${JAVA_OPTS[@]}"
+#  "-javaagent:path/to/opentelemetry-javaagent-all.jar"
+#)

Review comment:
       ```suggestion
   #JAVA_OPTS=("${JAVA_OPTS[@]}" 
"-javaagent:path/to/opentelemetry-javaagent-all.jar")
   ```

##########
File path: core/src/main/java/org/apache/accumulo/core/conf/Property.java
##########
@@ -763,6 +771,7 @@
       "The number of threads used to delete RFiles and write-ahead logs", 
"1.3.5"),
   GC_TRASH_IGNORE("gc.trash.ignore", "false", PropertyType.BOOLEAN,
       "Do not use the Trash, even if it is configured.", "1.5.0"),
+  @Deprecated(since = "2.1.0", forRemoval = true)

Review comment:
       These properties inconsistently use `forRemoval`. I like using it if we 
can get away with it, but in some cases, it can cause problems for certain IDEs 
(https://bugs.eclipse.org/bugs/show_bug.cgi?id=565271). If we can consistently 
use it without causing problems, I think that's better, but if we can't, it 
might be better to prioritize consistency over maximal usage of `forRemoval` 
where we can and avoiding it where we can't.

##########
File path: core/src/main/java/org/apache/accumulo/core/trace/TraceUtil.java
##########
@@ -18,211 +18,230 @@
  */
 package org.apache.accumulo.core.trace;
 
-import java.io.IOException;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Proxy;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Map;
-import java.util.Properties;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
+import java.util.Objects;
 
+import org.apache.accumulo.core.classloader.ClassLoaderUtil;
 import org.apache.accumulo.core.conf.AccumuloConfiguration;
-import org.apache.accumulo.core.conf.ClientProperty;
-import org.apache.accumulo.core.conf.ConfigurationTypeHelper;
 import org.apache.accumulo.core.conf.Property;
+import org.apache.accumulo.core.spi.trace.OpenTelemetryFactory;
 import org.apache.accumulo.core.trace.thrift.TInfo;
-import org.apache.hadoop.util.ShutdownHookManager;
-import org.apache.htrace.HTraceConfiguration;
-import org.apache.htrace.NullScope;
-import org.apache.htrace.Span;
-import org.apache.htrace.SpanReceiver;
-import org.apache.htrace.SpanReceiverBuilder;
-import org.apache.htrace.Trace;
-import org.apache.htrace.TraceInfo;
-import org.apache.htrace.TraceScope;
-import org.apache.htrace.impl.CountSampler;
-import org.apache.htrace.impl.ProbabilitySampler;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-/**
- * Utility class for tracing within Accumulo. Not intended for client use!
- */
+import io.opentelemetry.api.GlobalOpenTelemetry;
+import io.opentelemetry.api.OpenTelemetry;
+import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.trace.Span;
+import io.opentelemetry.api.trace.SpanBuilder;
+import io.opentelemetry.api.trace.SpanKind;
+import io.opentelemetry.api.trace.StatusCode;
+import io.opentelemetry.api.trace.Tracer;
+import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
+import io.opentelemetry.context.Context;
+import io.opentelemetry.context.Scope;
+import io.opentelemetry.context.propagation.TextMapGetter;
+import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
+
 public class TraceUtil {
 
-  private static final Logger log = LoggerFactory.getLogger(TraceUtil.class);
-  public static final String TRACE_HOST_PROPERTY = "trace.host";
-  public static final String TRACE_SERVICE_PROPERTY = "trace.service";
-  public static final String TRACER_ZK_HOST = "tracer.zookeeper.host";
-  public static final String TRACER_ZK_TIMEOUT = "tracer.zookeeper.timeout";
-  public static final String TRACER_ZK_PATH = "tracer.zookeeper.path";
-  private static final HashSet<SpanReceiver> receivers = new HashSet<>();
+  public static final Logger LOG = LoggerFactory.getLogger(TraceUtil.class);
+
+  private static final String SPAN_FORMAT = "%s::%s";
+
+  private static Tracer instance = null;
+  private static String name = null;
+  private static boolean tracing = false;
+
+  private static void initializeInternals(OpenTelemetry ot, String 
instrumentationName) {
+    // TODO: Is there a way to get our version to pass to getTracer() ?
+    name = instrumentationName;
+    instance = ot.getTracer(name);
+    tracing = (!ot.equals(OpenTelemetry.noop()));
+    LOG.info("Trace enabled: {}, OpenTelemetry instance: {}, Tracer instance: 
{}", tracing,
+        ot.getClass(), instance.getClass());
+  }
 
   /**
-   * Enable tracing by setting up SpanReceivers for the current process. If 
host name is null, it
-   * will be determined. If service name is null, the simple name of the class 
will be used.
-   * Properties required in the client configuration include
-   * {@link org.apache.accumulo.core.conf.ClientProperty#TRACE_SPAN_RECEIVERS} 
and any properties
-   * specific to the span receiver.
+   * Initialize TracerUtil using the OpenTelemetry parameter and 
instrumentationName
+   *
+   * @param ot
+   *          OpenTelemetry instance
+   * @param instrumentationName
+   *          OpenTelemetry instrumentation library name
    */
-  public static void enableClientTraces(String hostname, String service, 
Properties properties) {
-    // @formatter:off
-      enableTracing(hostname, service,
-          ClientProperty.TRACE_SPAN_RECEIVERS.getValue(properties),
-          ClientProperty.INSTANCE_ZOOKEEPERS.getValue(properties),
-          ConfigurationTypeHelper
-              
.getTimeInMillis(ClientProperty.INSTANCE_ZOOKEEPERS_TIMEOUT.getValue(properties)),
-          ClientProperty.TRACE_ZOOKEEPER_PATH.getValue(properties),
-          ClientProperty.toMap(
-              ClientProperty.getPrefix(properties, 
ClientProperty.TRACE_SPAN_RECEIVER_PREFIX)));
-      // @formatter:on
+  public static void initializeTracer(OpenTelemetry ot, String 
instrumentationName) {
+    if (instance != null) {
+      initializeInternals(ot, instrumentationName);
+    } else {
+      LOG.warn("Tracer already initialized.");
+    }
   }
 
   /**
-   * Enable tracing by setting up SpanReceivers for the current process. If 
host name is null, it
-   * will be determined. If service name is null, the simple name of the class 
will be used.
+   * Use the property values in the AccumuloConfiguration to call
+   * {@link #initializeTracer(boolean, String, String)}
+   *
+   * @param conf
+   *          AccumuloConfiguration
+   * @throws Exception
+   *           unable to find or load class
    */
-  public static void enableServerTraces(String hostname, String service,
-      AccumuloConfiguration conf) {
-    // @formatter:off
-      enableTracing(hostname, service,
-          conf.get(Property.TRACE_SPAN_RECEIVERS),
-          conf.get(Property.INSTANCE_ZK_HOST),
-          conf.getTimeInMillis(Property.INSTANCE_ZK_TIMEOUT),
-          conf.get(Property.TRACE_ZK_PATH),
-          
conf.getAllPropertiesWithPrefix(Property.TRACE_SPAN_RECEIVER_PREFIX));
-      // @formatter:on
+  public static void initializeTracer(AccumuloConfiguration conf) throws 
Exception {
+    initializeTracer(conf.getBoolean(Property.GENERAL_OPENTELEMETRY_ENABLED),
+        conf.get(Property.GENERAL_OPENTELEMETRY_FACTORY),
+        conf.get(Property.GENERAL_OPENTELEMETRY_NAME));
   }
 
-  private static void enableTracing(String hostname, String service, String 
spanReceivers,
-      String zookeepers, long timeout, String zkPath, Map<String,String> 
spanReceiverProps) {
-
-    Map<String,
-        String> htraceConfigProps = 
spanReceiverProps.entrySet().stream().collect(Collectors.toMap(
-            k -> 
String.valueOf(k).substring(Property.TRACE_SPAN_RECEIVER_PREFIX.getKey().length()),
-            String::valueOf, (a, b) -> {
-              throw new AssertionError("duplicate can't happen");
-            }, HashMap::new));
-    htraceConfigProps.put(TRACER_ZK_HOST, zookeepers);
-    htraceConfigProps.put(TRACER_ZK_TIMEOUT, Long.toString(timeout));
-    htraceConfigProps.put(TRACER_ZK_PATH, zkPath);
-    if (hostname != null) {
-      htraceConfigProps.put(TRACE_HOST_PROPERTY, hostname);
-    }
-    if (service != null) {
-      htraceConfigProps.put(TRACE_SERVICE_PROPERTY, service);
-    }
-    ShutdownHookManager.get().addShutdownHook(TraceUtil::disable, 0);
-    synchronized (receivers) {
-      if (!receivers.isEmpty()) {
-        log.info("Already loaded span receivers, enable tracing does not need 
to be called again");
-        return;
-      }
-      if (spanReceivers == null) {
-        return;
-      }
-      Stream.of(spanReceivers.split(",")).map(String::trim).filter(s -> 
!s.isEmpty())
-          .forEach(className -> {
-            HTraceConfiguration htraceConf = 
HTraceConfiguration.fromMap(htraceConfigProps);
-            SpanReceiverBuilder builder = new SpanReceiverBuilder(htraceConf);
-            SpanReceiver rcvr = 
builder.spanReceiverClass(className.trim()).build();
-            if (rcvr == null) {
-              log.warn("Failed to load SpanReceiver {}", className);
-            } else {
-              receivers.add(rcvr);
-              log.debug("SpanReceiver {} was loaded successfully.", className);
-            }
-          });
-      for (SpanReceiver rcvr : receivers) {
-        Trace.addReceiver(rcvr);
+  /**
+   * If not enabled, the OpenTelemetry implementation will be set to the NoOp 
implementation. If
+   * enabled and a factoryClass is supplied, then we will get the 
OpenTelemetry instance from the
+   * factory class.
+   *
+   * @param enabled
+   *          whether or not tracing is enabled
+   * @param factoryClass
+   *          name of class to load
+   * @param instrumentationName
+   *          OpenTelemetry instrumentation library name
+   * @throws Exception
+   *           unable to find or load class
+   */
+  public static void initializeTracer(boolean enabled, String factoryClass,
+      String instrumentationName) throws Exception {
+    if (instance == null) {
+      OpenTelemetry ot = null;
+      if (!enabled) {
+        ot = OpenTelemetry.noop();
+      } else if (factoryClass != null && !factoryClass.isEmpty()) {
+        Class<? extends OpenTelemetryFactory> clazz =
+            (Class<? extends OpenTelemetryFactory>) 
ClassLoaderUtil.loadClass(factoryClass,
+                OpenTelemetryFactory.class);

Review comment:
       The cast is redundant since loadClass is a generic method that returns 
the appropriate type based on the provided class parameter. In this case, it's 
probably fine to just use `var` as well.
   
   ```suggestion
           var clazz = ClassLoaderUtil.loadClass(factoryClass, 
OpenTelemetryFactory.class);
   ```

##########
File path: core/src/main/java/org/apache/accumulo/core/trace/TraceUtil.java
##########
@@ -18,211 +18,230 @@
  */
 package org.apache.accumulo.core.trace;
 
-import java.io.IOException;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Proxy;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Map;
-import java.util.Properties;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
+import java.util.Objects;
 
+import org.apache.accumulo.core.classloader.ClassLoaderUtil;
 import org.apache.accumulo.core.conf.AccumuloConfiguration;
-import org.apache.accumulo.core.conf.ClientProperty;
-import org.apache.accumulo.core.conf.ConfigurationTypeHelper;
 import org.apache.accumulo.core.conf.Property;
+import org.apache.accumulo.core.spi.trace.OpenTelemetryFactory;
 import org.apache.accumulo.core.trace.thrift.TInfo;
-import org.apache.hadoop.util.ShutdownHookManager;
-import org.apache.htrace.HTraceConfiguration;
-import org.apache.htrace.NullScope;
-import org.apache.htrace.Span;
-import org.apache.htrace.SpanReceiver;
-import org.apache.htrace.SpanReceiverBuilder;
-import org.apache.htrace.Trace;
-import org.apache.htrace.TraceInfo;
-import org.apache.htrace.TraceScope;
-import org.apache.htrace.impl.CountSampler;
-import org.apache.htrace.impl.ProbabilitySampler;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-/**
- * Utility class for tracing within Accumulo. Not intended for client use!
- */
+import io.opentelemetry.api.GlobalOpenTelemetry;
+import io.opentelemetry.api.OpenTelemetry;
+import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.trace.Span;
+import io.opentelemetry.api.trace.SpanBuilder;
+import io.opentelemetry.api.trace.SpanKind;
+import io.opentelemetry.api.trace.StatusCode;
+import io.opentelemetry.api.trace.Tracer;
+import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
+import io.opentelemetry.context.Context;
+import io.opentelemetry.context.Scope;
+import io.opentelemetry.context.propagation.TextMapGetter;
+import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
+
 public class TraceUtil {
 
-  private static final Logger log = LoggerFactory.getLogger(TraceUtil.class);
-  public static final String TRACE_HOST_PROPERTY = "trace.host";
-  public static final String TRACE_SERVICE_PROPERTY = "trace.service";
-  public static final String TRACER_ZK_HOST = "tracer.zookeeper.host";
-  public static final String TRACER_ZK_TIMEOUT = "tracer.zookeeper.timeout";
-  public static final String TRACER_ZK_PATH = "tracer.zookeeper.path";
-  private static final HashSet<SpanReceiver> receivers = new HashSet<>();
+  public static final Logger LOG = LoggerFactory.getLogger(TraceUtil.class);
+
+  private static final String SPAN_FORMAT = "%s::%s";
+
+  private static Tracer instance = null;
+  private static String name = null;
+  private static boolean tracing = false;
+
+  private static void initializeInternals(OpenTelemetry ot, String 
instrumentationName) {
+    // TODO: Is there a way to get our version to pass to getTracer() ?

Review comment:
       I'm not sure I understand this comment. What version are you referring 
to?

##########
File path: core/src/main/java/org/apache/accumulo/core/trace/TraceUtil.java
##########
@@ -18,211 +18,230 @@
  */
 package org.apache.accumulo.core.trace;
 
-import java.io.IOException;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Proxy;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Map;
-import java.util.Properties;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
+import java.util.Objects;
 
+import org.apache.accumulo.core.classloader.ClassLoaderUtil;
 import org.apache.accumulo.core.conf.AccumuloConfiguration;
-import org.apache.accumulo.core.conf.ClientProperty;
-import org.apache.accumulo.core.conf.ConfigurationTypeHelper;
 import org.apache.accumulo.core.conf.Property;
+import org.apache.accumulo.core.spi.trace.OpenTelemetryFactory;
 import org.apache.accumulo.core.trace.thrift.TInfo;
-import org.apache.hadoop.util.ShutdownHookManager;
-import org.apache.htrace.HTraceConfiguration;
-import org.apache.htrace.NullScope;
-import org.apache.htrace.Span;
-import org.apache.htrace.SpanReceiver;
-import org.apache.htrace.SpanReceiverBuilder;
-import org.apache.htrace.Trace;
-import org.apache.htrace.TraceInfo;
-import org.apache.htrace.TraceScope;
-import org.apache.htrace.impl.CountSampler;
-import org.apache.htrace.impl.ProbabilitySampler;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-/**
- * Utility class for tracing within Accumulo. Not intended for client use!
- */
+import io.opentelemetry.api.GlobalOpenTelemetry;
+import io.opentelemetry.api.OpenTelemetry;
+import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.trace.Span;
+import io.opentelemetry.api.trace.SpanBuilder;
+import io.opentelemetry.api.trace.SpanKind;
+import io.opentelemetry.api.trace.StatusCode;
+import io.opentelemetry.api.trace.Tracer;
+import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
+import io.opentelemetry.context.Context;
+import io.opentelemetry.context.Scope;
+import io.opentelemetry.context.propagation.TextMapGetter;
+import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
+
 public class TraceUtil {
 
-  private static final Logger log = LoggerFactory.getLogger(TraceUtil.class);
-  public static final String TRACE_HOST_PROPERTY = "trace.host";
-  public static final String TRACE_SERVICE_PROPERTY = "trace.service";
-  public static final String TRACER_ZK_HOST = "tracer.zookeeper.host";
-  public static final String TRACER_ZK_TIMEOUT = "tracer.zookeeper.timeout";
-  public static final String TRACER_ZK_PATH = "tracer.zookeeper.path";
-  private static final HashSet<SpanReceiver> receivers = new HashSet<>();
+  public static final Logger LOG = LoggerFactory.getLogger(TraceUtil.class);
+
+  private static final String SPAN_FORMAT = "%s::%s";
+
+  private static Tracer instance = null;
+  private static String name = null;
+  private static boolean tracing = false;
+
+  private static void initializeInternals(OpenTelemetry ot, String 
instrumentationName) {
+    // TODO: Is there a way to get our version to pass to getTracer() ?
+    name = instrumentationName;
+    instance = ot.getTracer(name);
+    tracing = (!ot.equals(OpenTelemetry.noop()));
+    LOG.info("Trace enabled: {}, OpenTelemetry instance: {}, Tracer instance: 
{}", tracing,
+        ot.getClass(), instance.getClass());
+  }
 
   /**
-   * Enable tracing by setting up SpanReceivers for the current process. If 
host name is null, it
-   * will be determined. If service name is null, the simple name of the class 
will be used.
-   * Properties required in the client configuration include
-   * {@link org.apache.accumulo.core.conf.ClientProperty#TRACE_SPAN_RECEIVERS} 
and any properties
-   * specific to the span receiver.
+   * Initialize TracerUtil using the OpenTelemetry parameter and 
instrumentationName
+   *
+   * @param ot
+   *          OpenTelemetry instance
+   * @param instrumentationName
+   *          OpenTelemetry instrumentation library name
    */
-  public static void enableClientTraces(String hostname, String service, 
Properties properties) {
-    // @formatter:off
-      enableTracing(hostname, service,
-          ClientProperty.TRACE_SPAN_RECEIVERS.getValue(properties),
-          ClientProperty.INSTANCE_ZOOKEEPERS.getValue(properties),
-          ConfigurationTypeHelper
-              
.getTimeInMillis(ClientProperty.INSTANCE_ZOOKEEPERS_TIMEOUT.getValue(properties)),
-          ClientProperty.TRACE_ZOOKEEPER_PATH.getValue(properties),
-          ClientProperty.toMap(
-              ClientProperty.getPrefix(properties, 
ClientProperty.TRACE_SPAN_RECEIVER_PREFIX)));
-      // @formatter:on
+  public static void initializeTracer(OpenTelemetry ot, String 
instrumentationName) {
+    if (instance != null) {
+      initializeInternals(ot, instrumentationName);
+    } else {
+      LOG.warn("Tracer already initialized.");
+    }
   }
 
   /**
-   * Enable tracing by setting up SpanReceivers for the current process. If 
host name is null, it
-   * will be determined. If service name is null, the simple name of the class 
will be used.
+   * Use the property values in the AccumuloConfiguration to call
+   * {@link #initializeTracer(boolean, String, String)}
+   *
+   * @param conf
+   *          AccumuloConfiguration
+   * @throws Exception
+   *           unable to find or load class
    */
-  public static void enableServerTraces(String hostname, String service,
-      AccumuloConfiguration conf) {
-    // @formatter:off
-      enableTracing(hostname, service,
-          conf.get(Property.TRACE_SPAN_RECEIVERS),
-          conf.get(Property.INSTANCE_ZK_HOST),
-          conf.getTimeInMillis(Property.INSTANCE_ZK_TIMEOUT),
-          conf.get(Property.TRACE_ZK_PATH),
-          
conf.getAllPropertiesWithPrefix(Property.TRACE_SPAN_RECEIVER_PREFIX));
-      // @formatter:on
+  public static void initializeTracer(AccumuloConfiguration conf) throws 
Exception {
+    initializeTracer(conf.getBoolean(Property.GENERAL_OPENTELEMETRY_ENABLED),
+        conf.get(Property.GENERAL_OPENTELEMETRY_FACTORY),
+        conf.get(Property.GENERAL_OPENTELEMETRY_NAME));
   }
 
-  private static void enableTracing(String hostname, String service, String 
spanReceivers,
-      String zookeepers, long timeout, String zkPath, Map<String,String> 
spanReceiverProps) {
-
-    Map<String,
-        String> htraceConfigProps = 
spanReceiverProps.entrySet().stream().collect(Collectors.toMap(
-            k -> 
String.valueOf(k).substring(Property.TRACE_SPAN_RECEIVER_PREFIX.getKey().length()),
-            String::valueOf, (a, b) -> {
-              throw new AssertionError("duplicate can't happen");
-            }, HashMap::new));
-    htraceConfigProps.put(TRACER_ZK_HOST, zookeepers);
-    htraceConfigProps.put(TRACER_ZK_TIMEOUT, Long.toString(timeout));
-    htraceConfigProps.put(TRACER_ZK_PATH, zkPath);
-    if (hostname != null) {
-      htraceConfigProps.put(TRACE_HOST_PROPERTY, hostname);
-    }
-    if (service != null) {
-      htraceConfigProps.put(TRACE_SERVICE_PROPERTY, service);
-    }
-    ShutdownHookManager.get().addShutdownHook(TraceUtil::disable, 0);
-    synchronized (receivers) {
-      if (!receivers.isEmpty()) {
-        log.info("Already loaded span receivers, enable tracing does not need 
to be called again");
-        return;
-      }
-      if (spanReceivers == null) {
-        return;
-      }
-      Stream.of(spanReceivers.split(",")).map(String::trim).filter(s -> 
!s.isEmpty())
-          .forEach(className -> {
-            HTraceConfiguration htraceConf = 
HTraceConfiguration.fromMap(htraceConfigProps);
-            SpanReceiverBuilder builder = new SpanReceiverBuilder(htraceConf);
-            SpanReceiver rcvr = 
builder.spanReceiverClass(className.trim()).build();
-            if (rcvr == null) {
-              log.warn("Failed to load SpanReceiver {}", className);
-            } else {
-              receivers.add(rcvr);
-              log.debug("SpanReceiver {} was loaded successfully.", className);
-            }
-          });
-      for (SpanReceiver rcvr : receivers) {
-        Trace.addReceiver(rcvr);
+  /**
+   * If not enabled, the OpenTelemetry implementation will be set to the NoOp 
implementation. If
+   * enabled and a factoryClass is supplied, then we will get the 
OpenTelemetry instance from the
+   * factory class.
+   *
+   * @param enabled
+   *          whether or not tracing is enabled
+   * @param factoryClass
+   *          name of class to load
+   * @param instrumentationName
+   *          OpenTelemetry instrumentation library name
+   * @throws Exception
+   *           unable to find or load class
+   */
+  public static void initializeTracer(boolean enabled, String factoryClass,
+      String instrumentationName) throws Exception {
+    if (instance == null) {
+      OpenTelemetry ot = null;
+      if (!enabled) {
+        ot = OpenTelemetry.noop();
+      } else if (factoryClass != null && !factoryClass.isEmpty()) {
+        Class<? extends OpenTelemetryFactory> clazz =
+            (Class<? extends OpenTelemetryFactory>) 
ClassLoaderUtil.loadClass(factoryClass,
+                OpenTelemetryFactory.class);
+        OpenTelemetryFactory factory = 
clazz.getDeclaredConstructor().newInstance();
+        ot = factory.getOpenTelemetry();
+        LOG.info("OpenTelemetry configured and set from {}", clazz);
+      } else {
+        ot = GlobalOpenTelemetry.get();
       }
+      initializeInternals(ot, instrumentationName);
+    } else {
+      LOG.warn("Tracer already initialized.");
     }
   }
 
   /**
-   * Disable tracing by closing SpanReceivers for the current process.
+   * @return the Tracer set on the GlobalOpenTelemetry object
    */
-  public static void disable() {
-    synchronized (receivers) {
-      receivers.forEach(rcvr -> {
-        try {
-          rcvr.close();
-        } catch (IOException e) {
-          log.warn("Unable to close SpanReceiver correctly: {}", 
e.getMessage(), e);
-        }
-      });
-      receivers.clear();
+  private static Tracer getTracer() {
+    if (Objects.isNull(instance)) {

Review comment:
       It's more obvious what is happening when `== null` is used directly, 
instead of adding to the call stack to do the check indirectly in another 
method. `Objects.isNull` is primarily useful as a Predicate, as in 
`someObj.removeIf(Objects::isNull)` rather than as a substitute for `== null`.
   
   ```suggestion
       if (instance == null) {
   ```

##########
File path: core/src/main/java/org/apache/accumulo/core/trace/TraceUtil.java
##########
@@ -18,211 +18,230 @@
  */
 package org.apache.accumulo.core.trace;
 
-import java.io.IOException;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Proxy;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Map;
-import java.util.Properties;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
+import java.util.Objects;
 
+import org.apache.accumulo.core.classloader.ClassLoaderUtil;
 import org.apache.accumulo.core.conf.AccumuloConfiguration;
-import org.apache.accumulo.core.conf.ClientProperty;
-import org.apache.accumulo.core.conf.ConfigurationTypeHelper;
 import org.apache.accumulo.core.conf.Property;
+import org.apache.accumulo.core.spi.trace.OpenTelemetryFactory;
 import org.apache.accumulo.core.trace.thrift.TInfo;
-import org.apache.hadoop.util.ShutdownHookManager;
-import org.apache.htrace.HTraceConfiguration;
-import org.apache.htrace.NullScope;
-import org.apache.htrace.Span;
-import org.apache.htrace.SpanReceiver;
-import org.apache.htrace.SpanReceiverBuilder;
-import org.apache.htrace.Trace;
-import org.apache.htrace.TraceInfo;
-import org.apache.htrace.TraceScope;
-import org.apache.htrace.impl.CountSampler;
-import org.apache.htrace.impl.ProbabilitySampler;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-/**
- * Utility class for tracing within Accumulo. Not intended for client use!
- */
+import io.opentelemetry.api.GlobalOpenTelemetry;
+import io.opentelemetry.api.OpenTelemetry;
+import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.trace.Span;
+import io.opentelemetry.api.trace.SpanBuilder;
+import io.opentelemetry.api.trace.SpanKind;
+import io.opentelemetry.api.trace.StatusCode;
+import io.opentelemetry.api.trace.Tracer;
+import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
+import io.opentelemetry.context.Context;
+import io.opentelemetry.context.Scope;
+import io.opentelemetry.context.propagation.TextMapGetter;
+import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
+
 public class TraceUtil {
 
-  private static final Logger log = LoggerFactory.getLogger(TraceUtil.class);
-  public static final String TRACE_HOST_PROPERTY = "trace.host";
-  public static final String TRACE_SERVICE_PROPERTY = "trace.service";
-  public static final String TRACER_ZK_HOST = "tracer.zookeeper.host";
-  public static final String TRACER_ZK_TIMEOUT = "tracer.zookeeper.timeout";
-  public static final String TRACER_ZK_PATH = "tracer.zookeeper.path";
-  private static final HashSet<SpanReceiver> receivers = new HashSet<>();
+  public static final Logger LOG = LoggerFactory.getLogger(TraceUtil.class);
+
+  private static final String SPAN_FORMAT = "%s::%s";
+
+  private static Tracer instance = null;
+  private static String name = null;
+  private static boolean tracing = false;
+
+  private static void initializeInternals(OpenTelemetry ot, String 
instrumentationName) {
+    // TODO: Is there a way to get our version to pass to getTracer() ?
+    name = instrumentationName;
+    instance = ot.getTracer(name);
+    tracing = (!ot.equals(OpenTelemetry.noop()));

Review comment:
       ```suggestion
       tracing = !ot.equals(OpenTelemetry.noop());
   ```

##########
File path: 
core/src/test/java/org/apache/accumulo/core/conf/cluster/ClusterConfigParserTest.java
##########
@@ -48,13 +48,12 @@ public void testParse() throws Exception {
 
     Map<String,String> contents =
         ClusterConfigParser.parseConfiguration(new 
File(configFile.toURI()).getAbsolutePath());
-    assertEquals(5, contents.size());
+    assertEquals(4, contents.size());
     assertTrue(contents.containsKey("manager"));
     assertEquals("localhost1 localhost2", contents.get("manager"));
     assertTrue(contents.containsKey("monitor"));
     assertEquals("localhost1 localhost2", contents.get("monitor"));
-    assertTrue(contents.containsKey("tracer"));
-    assertEquals("localhost", contents.get("tracer"));
+    assertFalse(contents.containsKey("tracer"));

Review comment:
       Not really needed. We already checked that the contents are size 4, and 
we have a separate assert for each of those 4.
   
   ```suggestion
   ```

##########
File path: core/src/main/java/org/apache/accumulo/core/trace/TraceUtil.java
##########
@@ -18,211 +18,230 @@
  */
 package org.apache.accumulo.core.trace;
 
-import java.io.IOException;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Proxy;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Map;
-import java.util.Properties;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
+import java.util.Objects;
 
+import org.apache.accumulo.core.classloader.ClassLoaderUtil;
 import org.apache.accumulo.core.conf.AccumuloConfiguration;
-import org.apache.accumulo.core.conf.ClientProperty;
-import org.apache.accumulo.core.conf.ConfigurationTypeHelper;
 import org.apache.accumulo.core.conf.Property;
+import org.apache.accumulo.core.spi.trace.OpenTelemetryFactory;
 import org.apache.accumulo.core.trace.thrift.TInfo;
-import org.apache.hadoop.util.ShutdownHookManager;
-import org.apache.htrace.HTraceConfiguration;
-import org.apache.htrace.NullScope;
-import org.apache.htrace.Span;
-import org.apache.htrace.SpanReceiver;
-import org.apache.htrace.SpanReceiverBuilder;
-import org.apache.htrace.Trace;
-import org.apache.htrace.TraceInfo;
-import org.apache.htrace.TraceScope;
-import org.apache.htrace.impl.CountSampler;
-import org.apache.htrace.impl.ProbabilitySampler;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-/**
- * Utility class for tracing within Accumulo. Not intended for client use!
- */
+import io.opentelemetry.api.GlobalOpenTelemetry;
+import io.opentelemetry.api.OpenTelemetry;
+import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.trace.Span;
+import io.opentelemetry.api.trace.SpanBuilder;
+import io.opentelemetry.api.trace.SpanKind;
+import io.opentelemetry.api.trace.StatusCode;
+import io.opentelemetry.api.trace.Tracer;
+import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
+import io.opentelemetry.context.Context;
+import io.opentelemetry.context.Scope;
+import io.opentelemetry.context.propagation.TextMapGetter;
+import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
+
 public class TraceUtil {
 
-  private static final Logger log = LoggerFactory.getLogger(TraceUtil.class);
-  public static final String TRACE_HOST_PROPERTY = "trace.host";
-  public static final String TRACE_SERVICE_PROPERTY = "trace.service";
-  public static final String TRACER_ZK_HOST = "tracer.zookeeper.host";
-  public static final String TRACER_ZK_TIMEOUT = "tracer.zookeeper.timeout";
-  public static final String TRACER_ZK_PATH = "tracer.zookeeper.path";
-  private static final HashSet<SpanReceiver> receivers = new HashSet<>();
+  public static final Logger LOG = LoggerFactory.getLogger(TraceUtil.class);
+
+  private static final String SPAN_FORMAT = "%s::%s";
+
+  private static Tracer instance = null;
+  private static String name = null;
+  private static boolean tracing = false;
+
+  private static void initializeInternals(OpenTelemetry ot, String 
instrumentationName) {
+    // TODO: Is there a way to get our version to pass to getTracer() ?
+    name = instrumentationName;
+    instance = ot.getTracer(name);
+    tracing = (!ot.equals(OpenTelemetry.noop()));
+    LOG.info("Trace enabled: {}, OpenTelemetry instance: {}, Tracer instance: 
{}", tracing,
+        ot.getClass(), instance.getClass());
+  }
 
   /**
-   * Enable tracing by setting up SpanReceivers for the current process. If 
host name is null, it
-   * will be determined. If service name is null, the simple name of the class 
will be used.
-   * Properties required in the client configuration include
-   * {@link org.apache.accumulo.core.conf.ClientProperty#TRACE_SPAN_RECEIVERS} 
and any properties
-   * specific to the span receiver.
+   * Initialize TracerUtil using the OpenTelemetry parameter and 
instrumentationName
+   *
+   * @param ot
+   *          OpenTelemetry instance
+   * @param instrumentationName
+   *          OpenTelemetry instrumentation library name
    */
-  public static void enableClientTraces(String hostname, String service, 
Properties properties) {
-    // @formatter:off
-      enableTracing(hostname, service,
-          ClientProperty.TRACE_SPAN_RECEIVERS.getValue(properties),
-          ClientProperty.INSTANCE_ZOOKEEPERS.getValue(properties),
-          ConfigurationTypeHelper
-              
.getTimeInMillis(ClientProperty.INSTANCE_ZOOKEEPERS_TIMEOUT.getValue(properties)),
-          ClientProperty.TRACE_ZOOKEEPER_PATH.getValue(properties),
-          ClientProperty.toMap(
-              ClientProperty.getPrefix(properties, 
ClientProperty.TRACE_SPAN_RECEIVER_PREFIX)));
-      // @formatter:on
+  public static void initializeTracer(OpenTelemetry ot, String 
instrumentationName) {
+    if (instance != null) {
+      initializeInternals(ot, instrumentationName);
+    } else {
+      LOG.warn("Tracer already initialized.");
+    }
   }
 
   /**
-   * Enable tracing by setting up SpanReceivers for the current process. If 
host name is null, it
-   * will be determined. If service name is null, the simple name of the class 
will be used.
+   * Use the property values in the AccumuloConfiguration to call
+   * {@link #initializeTracer(boolean, String, String)}
+   *
+   * @param conf
+   *          AccumuloConfiguration
+   * @throws Exception
+   *           unable to find or load class
    */
-  public static void enableServerTraces(String hostname, String service,
-      AccumuloConfiguration conf) {
-    // @formatter:off
-      enableTracing(hostname, service,
-          conf.get(Property.TRACE_SPAN_RECEIVERS),
-          conf.get(Property.INSTANCE_ZK_HOST),
-          conf.getTimeInMillis(Property.INSTANCE_ZK_TIMEOUT),
-          conf.get(Property.TRACE_ZK_PATH),
-          
conf.getAllPropertiesWithPrefix(Property.TRACE_SPAN_RECEIVER_PREFIX));
-      // @formatter:on
+  public static void initializeTracer(AccumuloConfiguration conf) throws 
Exception {
+    initializeTracer(conf.getBoolean(Property.GENERAL_OPENTELEMETRY_ENABLED),
+        conf.get(Property.GENERAL_OPENTELEMETRY_FACTORY),
+        conf.get(Property.GENERAL_OPENTELEMETRY_NAME));
   }
 
-  private static void enableTracing(String hostname, String service, String 
spanReceivers,
-      String zookeepers, long timeout, String zkPath, Map<String,String> 
spanReceiverProps) {
-
-    Map<String,
-        String> htraceConfigProps = 
spanReceiverProps.entrySet().stream().collect(Collectors.toMap(
-            k -> 
String.valueOf(k).substring(Property.TRACE_SPAN_RECEIVER_PREFIX.getKey().length()),
-            String::valueOf, (a, b) -> {
-              throw new AssertionError("duplicate can't happen");
-            }, HashMap::new));
-    htraceConfigProps.put(TRACER_ZK_HOST, zookeepers);
-    htraceConfigProps.put(TRACER_ZK_TIMEOUT, Long.toString(timeout));
-    htraceConfigProps.put(TRACER_ZK_PATH, zkPath);
-    if (hostname != null) {
-      htraceConfigProps.put(TRACE_HOST_PROPERTY, hostname);
-    }
-    if (service != null) {
-      htraceConfigProps.put(TRACE_SERVICE_PROPERTY, service);
-    }
-    ShutdownHookManager.get().addShutdownHook(TraceUtil::disable, 0);
-    synchronized (receivers) {
-      if (!receivers.isEmpty()) {
-        log.info("Already loaded span receivers, enable tracing does not need 
to be called again");
-        return;
-      }
-      if (spanReceivers == null) {
-        return;
-      }
-      Stream.of(spanReceivers.split(",")).map(String::trim).filter(s -> 
!s.isEmpty())
-          .forEach(className -> {
-            HTraceConfiguration htraceConf = 
HTraceConfiguration.fromMap(htraceConfigProps);
-            SpanReceiverBuilder builder = new SpanReceiverBuilder(htraceConf);
-            SpanReceiver rcvr = 
builder.spanReceiverClass(className.trim()).build();
-            if (rcvr == null) {
-              log.warn("Failed to load SpanReceiver {}", className);
-            } else {
-              receivers.add(rcvr);
-              log.debug("SpanReceiver {} was loaded successfully.", className);
-            }
-          });
-      for (SpanReceiver rcvr : receivers) {
-        Trace.addReceiver(rcvr);
+  /**
+   * If not enabled, the OpenTelemetry implementation will be set to the NoOp 
implementation. If
+   * enabled and a factoryClass is supplied, then we will get the 
OpenTelemetry instance from the
+   * factory class.
+   *
+   * @param enabled
+   *          whether or not tracing is enabled
+   * @param factoryClass
+   *          name of class to load
+   * @param instrumentationName
+   *          OpenTelemetry instrumentation library name
+   * @throws Exception
+   *           unable to find or load class
+   */
+  public static void initializeTracer(boolean enabled, String factoryClass,
+      String instrumentationName) throws Exception {
+    if (instance == null) {
+      OpenTelemetry ot = null;
+      if (!enabled) {
+        ot = OpenTelemetry.noop();
+      } else if (factoryClass != null && !factoryClass.isEmpty()) {
+        Class<? extends OpenTelemetryFactory> clazz =
+            (Class<? extends OpenTelemetryFactory>) 
ClassLoaderUtil.loadClass(factoryClass,
+                OpenTelemetryFactory.class);
+        OpenTelemetryFactory factory = 
clazz.getDeclaredConstructor().newInstance();
+        ot = factory.getOpenTelemetry();
+        LOG.info("OpenTelemetry configured and set from {}", clazz);
+      } else {
+        ot = GlobalOpenTelemetry.get();
       }
+      initializeInternals(ot, instrumentationName);
+    } else {
+      LOG.warn("Tracer already initialized.");
     }
   }
 
   /**
-   * Disable tracing by closing SpanReceivers for the current process.
+   * @return the Tracer set on the GlobalOpenTelemetry object
    */
-  public static void disable() {
-    synchronized (receivers) {
-      receivers.forEach(rcvr -> {
-        try {
-          rcvr.close();
-        } catch (IOException e) {
-          log.warn("Unable to close SpanReceiver correctly: {}", 
e.getMessage(), e);
-        }
-      });
-      receivers.clear();
+  private static Tracer getTracer() {
+    if (Objects.isNull(instance)) {
+      LOG.warn("initializeTracer not called, using 
GlobalOpenTelemetry.getTracer()");
+      instance = GlobalOpenTelemetry.getTracer(name);
+      tracing = (!instance.equals(OpenTelemetry.noop().getTracer(name)));
+      LOG.info("Trace enabled: {}, Tracer is: {}", tracing, 
instance.getClass());
     }
+    return instance;
   }
 
   /**
-   * Continue a trace by starting a new span with a given parent and 
description.
+   * @return true if an OpenTelemetry Tracer implementation has been set, 
false if the NoOp Tracer
+   *         is being used.
    */
-  public static TraceScope trace(TInfo info, String description) {
-    return info.traceId == 0 ? NullScope.INSTANCE
-        : Trace.startSpan(description, new TraceInfo(info.traceId, 
info.parentId));
+  public static boolean isTracing() {
+    return tracing;
   }
 
-  private static final TInfo DONT_TRACE = new TInfo(0, 0);
+  public static Span createSpan(Class<?> caller, String spanName, SpanKind 
kind) {
+    return createSpan(caller, spanName, kind, null, false, null);
+  }
 
-  /**
-   * Obtain {@link org.apache.accumulo.core.trace.thrift.TInfo} for the 
current span.
-   */
-  public static TInfo traceInfo() {
-    Span span = Trace.currentSpan();
-    if (span != null) {
-      return new TInfo(span.getTraceId(), span.getSpanId());
+  public static Span createSpan(Class<?> caller, String spanName, SpanKind 
kind, Context parent) {
+    return createSpan(caller, spanName, kind, null, false, parent);
+  }
+
+  public static Span createSpan(Class<?> caller, String spanName, SpanKind 
kind,
+      Map<String,String> attributes) {
+    return createSpan(caller, spanName, kind, attributes, false, null);
+  }
+
+  public static Span createSpan(Class<?> caller, String spanName, SpanKind 
kind,
+      Map<String,String> attributes, boolean setNoParent, Context parent) {
+    final String name = String.format(SPAN_FORMAT, caller.getSimpleName(), 
spanName);
+    final SpanBuilder builder = getTracer().spanBuilder(name);
+    builder.setSpanKind(kind);
+    if (attributes != null) {
+      attributes.forEach((k, v) -> builder.setAttribute(k, v));
     }
-    return DONT_TRACE;
+    if (setNoParent) {
+      builder.setNoParent();
+    } else if (parent != null) {
+      builder.setParent(parent);
+    }
+    return builder.startSpan();
   }
 
-  public static CountSampler countSampler(long frequency) {
-    return new CountSampler(HTraceConfiguration.fromMap(Collections
-        .singletonMap(CountSampler.SAMPLER_FREQUENCY_CONF_KEY, 
Long.toString(frequency))));
+  /**
+   * Record that an Exception occurred in the code covered by a Span
+   *
+   * @param span
+   *          the span
+   * @param e
+   *          the exception
+   * @param rethrown
+   *          whether the exception is subsequently re-thrown
+   */
+  public static void setException(Span span, Throwable e, boolean rethrown) {
+    if (tracing) {
+      span.setStatus(StatusCode.ERROR);
+      span.recordException(e,
+          Attributes.builder().put(SemanticAttributes.EXCEPTION_TYPE, 
e.getClass().getName())
+              .put(SemanticAttributes.EXCEPTION_MESSAGE, e.getMessage())

Review comment:
       I'm surprised the exception type and message aren't automatically 
recorded by the fact that we've provided the actual exception, `e` as the first 
param.

##########
File path: 
minicluster/src/main/java/org/apache/accumulo/miniclusterImpl/MiniAccumuloConfigImpl.java
##########
@@ -133,12 +133,14 @@ MiniAccumuloConfigImpl initialize() {
       zooKeeperDir = new File(dir, "zookeeper");
       logDir = new File(dir, "logs");
 
+      @SuppressWarnings("deprecation")
+      final String traceTokenPropertyPrefixKey = 
Property.TRACE_TOKEN_PROPERTY_PREFIX.getKey();
       // Never want to override these if an existing instance, which may be 
using the defaults
       if (existingInstance == null || !existingInstance) {
         existingInstance = false;
         mergeProp(Property.INSTANCE_VOLUMES.getKey(), "file://" + 
accumuloDir.getAbsolutePath());
         mergeProp(Property.INSTANCE_SECRET.getKey(), DEFAULT_INSTANCE_SECRET);
-        mergeProp(Property.TRACE_TOKEN_PROPERTY_PREFIX.getKey() + "password", 
getRootPassword());
+        mergeProp(traceTokenPropertyPrefixKey + "password", getRootPassword());

Review comment:
       We don't need to write this to the file at all anymore, since it is 
unused now.
   
   ```suggestion
   ```

##########
File path: 
server/base/src/main/java/org/apache/accumulo/server/AbstractServer.java
##########
@@ -49,7 +51,11 @@ protected AbstractServer(String appName, ServerOpts opts, 
String[] args) {
     log.info("Instance " + context.getInstanceID());
     context.init(appName);
     ClassLoaderUtil.initContextFactory(context.getConfiguration());
-    TraceUtil.enableServerTraces(hostname, appName, 
context.getConfiguration());
+    try {
+      TraceUtil.initializeTracer(context.getConfiguration());
+    } catch (Exception e) {
+      log.error("Error initializing tracing", e);
+    }

Review comment:
       TraceUtil could handle initialization errors itself, and not throw 
anything.

##########
File path: 
shell/src/main/java/org/apache/accumulo/shell/commands/TraceCommand.java
##########
@@ -18,80 +18,34 @@
  */
 package org.apache.accumulo.shell.commands;
 
-import static 
org.apache.accumulo.fate.util.UtilWaitThread.sleepUninterruptibly;
-
 import java.io.IOException;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
 
-import org.apache.accumulo.core.client.Scanner;
-import org.apache.accumulo.core.conf.Property;
-import org.apache.accumulo.core.data.Range;
-import org.apache.accumulo.core.security.Authorizations;
+import org.apache.accumulo.core.trace.TraceUtil;
 import org.apache.accumulo.core.util.BadArgumentException;
 import org.apache.accumulo.shell.Shell;
 import org.apache.accumulo.shell.Shell.Command;
-import org.apache.accumulo.tracer.TraceDump;
 import org.apache.commons.cli.CommandLine;
-import org.apache.hadoop.io.Text;
-import org.apache.htrace.Sampler;
-import org.apache.htrace.Trace;
-import org.apache.htrace.TraceScope;
+
+import io.opentelemetry.api.trace.Span;
+import io.opentelemetry.api.trace.SpanKind;
 
 public class TraceCommand extends Command {
 
-  private TraceScope traceScope = null;
+  private Span span = null;
 
   @Override
   public int execute(final String fullCommand, final CommandLine cl, final 
Shell shellState)
       throws IOException {
     if (cl.getArgs().length == 1) {
       if (cl.getArgs()[0].equalsIgnoreCase("on")) {
-        if (traceScope == null) {
-          traceScope =
-              Trace.startSpan("shell:" + 
shellState.getAccumuloClient().whoami(), Sampler.ALWAYS);
+        if (span == null) {
+          span = TraceUtil.createSpan(Shell.class, 
shellState.getAccumuloClient().whoami(),
+              SpanKind.CLIENT);
         }
       } else if (cl.getArgs()[0].equalsIgnoreCase("off")) {
-        if (traceScope != null) {
-          final long trace = traceScope.getSpan().getTraceId();
-          traceScope.close();
-          traceScope = null;
-          StringBuilder sb = new StringBuilder();
-          int traceCount = 0;
-          for (int i = 0; i < 30; i++) {
-            sb = new StringBuilder();
-            try {
-              final Map<String,String> properties =
-                  
shellState.getAccumuloClient().instanceOperations().getSystemConfiguration();
-              final String table = 
properties.get(Property.TRACE_TABLE.getKey());
-              final String user = shellState.getAccumuloClient().whoami();
-              final Authorizations auths =
-                  
shellState.getAccumuloClient().securityOperations().getUserAuthorizations(user);
-              final Scanner scanner = 
shellState.getAccumuloClient().createScanner(table, auths);
-              scanner.setRange(new Range(new Text(Long.toHexString(trace))));
-              final StringBuilder finalSB = sb;
-              traceCount = TraceDump.printTrace(scanner, line -> {
-                try {
-                  finalSB.append(line + "\n");
-                } catch (Exception ex) {
-                  throw new RuntimeException(ex);
-                }
-              });
-              if (traceCount > 0) {
-                shellState.getWriter().print(sb.toString());
-                break;
-              }
-            } catch (Exception ex) {
-              shellState.printException(ex);
-            }
-            shellState.getWriter().println("Waiting for trace information");
-            shellState.getWriter().flush();
-            sleepUninterruptibly(500, TimeUnit.MILLISECONDS);
-          }
-          if (traceCount < 0) {
-            // display the trace even though there are unrooted spans
-            shellState.getWriter().print(sb.toString());
-          }
+        if (span != null) {
+          span.end();
+          span = null;

Review comment:
       I don't think the changes to this class preserve what was originally 
intended. I think what was originally intended was something like swapping out 
the global tracer with the NOOP tracer when set to `off`, and using whatever 
configured tracer was initialized if the tracer was set to `on`. If it were 
possible to set the sampler here, we could just grab a Tracer configured with a 
custom sampler that just read the value of a static AtomicBoolean in TraceUtil. 
But, to set the sampler, we'd need to use the SDK, which limits the 
pluggability of the OpenTelemetry instance.

##########
File path: 
minicluster/src/main/java/org/apache/accumulo/miniclusterImpl/MiniAccumuloConfigImpl.java
##########
@@ -133,12 +133,14 @@ MiniAccumuloConfigImpl initialize() {
       zooKeeperDir = new File(dir, "zookeeper");
       logDir = new File(dir, "logs");
 
+      @SuppressWarnings("deprecation")
+      final String traceTokenPropertyPrefixKey = 
Property.TRACE_TOKEN_PROPERTY_PREFIX.getKey();

Review comment:
       ```suggestion
   ```

##########
File path: 
server/monitor/src/main/java/org/apache/accumulo/monitor/rest/trace/RecentTracesInformation.java
##########
@@ -51,25 +49,4 @@ public RecentTracesInformation(String type) {
     this.type = type;
   }
 
-  /**
-   * Adds a span for the trace
-   *
-   * @param span
-   *          Remote span to obtain information
-   */
-  public void addSpan(RemoteSpan span) {
-    total++;
-    long ms = span.stop - span.start;
-    totalMS += ms;

Review comment:
       totalMS is now unused and should be deleted also.

##########
File path: core/src/main/java/org/apache/accumulo/core/conf/Property.java
##########
@@ -239,6 +239,12 @@
       "1.6.5"),
   GENERAL_MAX_MESSAGE_SIZE("general.server.message.size.max", "1G", 
PropertyType.BYTES,
       "The maximum size of a message that can be sent to a server.", "1.5.0"),
+  GENERAL_OPENTELEMETRY_ENABLED("general.opentelemetry.enabled", "false", 
PropertyType.BOOLEAN,
+      "Enables tracing functionality using OpenTelemetry.", "2.1.0"),
+  GENERAL_OPENTELEMETRY_FACTORY("general.opentelemetry.factory", "", 
PropertyType.CLASSNAME,
+      "Name of class that implements OpenTelemetryFactory", "2.1.0"),
+  GENERAL_OPENTELEMETRY_NAME("general.opentelemetry.instrumentation.name", "", 
PropertyType.STRING,
+      "Name of the instrumentation library to use.", "2.1.0"),

Review comment:
       I see that there is updated documentation at 
https://opentelemetry.io/docs/java/manual_instrumentation/#tracing that further 
explains, with links to a glossary the difference between the "instrumentation 
library" and the "instrumented library", clarifying that they are the same in 
the case of manual instrumentation, such as what we're doing. So, we can just 
pass in "org.apache.accumulo" as the hard-coded name of the instrumentation and 
our current version, when we call `getTracer(name, version)` from the 
OpenTelemetry instance that is supplied to us.
   
   There's also some interesting `@WithSpan` annotations that we could use from 
the [OpenTelemetry extension 
annotations](https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/docs/manual-instrumentation.md)

##########
File path: core/src/main/java/org/apache/accumulo/core/conf/Property.java
##########
@@ -239,6 +239,12 @@
       "1.6.5"),
   GENERAL_MAX_MESSAGE_SIZE("general.server.message.size.max", "1G", 
PropertyType.BYTES,
       "The maximum size of a message that can be sent to a server.", "1.5.0"),
+  GENERAL_OPENTELEMETRY_ENABLED("general.opentelemetry.enabled", "false", 
PropertyType.BOOLEAN,
+      "Enables tracing functionality using OpenTelemetry.", "2.1.0"),
+  GENERAL_OPENTELEMETRY_FACTORY("general.opentelemetry.factory", "", 
PropertyType.CLASSNAME,
+      "Name of class that implements OpenTelemetryFactory", "2.1.0"),
+  GENERAL_OPENTELEMETRY_NAME("general.opentelemetry.instrumentation.name", "", 
PropertyType.STRING,
+      "Name of the instrumentation library to use.", "2.1.0"),

Review comment:
       Because OpenTelemetry is still somewhat new, and it's definitely new to 
us, I think it might be a good idea to mark these as `@Experimental` for at 
least a minor release, until we're highly confident that this set of properties 
will be stable. Same goes for the corresponding client properties.

##########
File path: core/src/main/java/org/apache/accumulo/core/trace/TraceUtil.java
##########
@@ -18,211 +18,230 @@
  */
 package org.apache.accumulo.core.trace;
 
-import java.io.IOException;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Proxy;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Map;
-import java.util.Properties;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
+import java.util.Objects;
 
+import org.apache.accumulo.core.classloader.ClassLoaderUtil;
 import org.apache.accumulo.core.conf.AccumuloConfiguration;
-import org.apache.accumulo.core.conf.ClientProperty;
-import org.apache.accumulo.core.conf.ConfigurationTypeHelper;
 import org.apache.accumulo.core.conf.Property;
+import org.apache.accumulo.core.spi.trace.OpenTelemetryFactory;
 import org.apache.accumulo.core.trace.thrift.TInfo;
-import org.apache.hadoop.util.ShutdownHookManager;
-import org.apache.htrace.HTraceConfiguration;
-import org.apache.htrace.NullScope;
-import org.apache.htrace.Span;
-import org.apache.htrace.SpanReceiver;
-import org.apache.htrace.SpanReceiverBuilder;
-import org.apache.htrace.Trace;
-import org.apache.htrace.TraceInfo;
-import org.apache.htrace.TraceScope;
-import org.apache.htrace.impl.CountSampler;
-import org.apache.htrace.impl.ProbabilitySampler;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-/**
- * Utility class for tracing within Accumulo. Not intended for client use!
- */
+import io.opentelemetry.api.GlobalOpenTelemetry;
+import io.opentelemetry.api.OpenTelemetry;
+import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.trace.Span;
+import io.opentelemetry.api.trace.SpanBuilder;
+import io.opentelemetry.api.trace.SpanKind;
+import io.opentelemetry.api.trace.StatusCode;
+import io.opentelemetry.api.trace.Tracer;
+import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
+import io.opentelemetry.context.Context;
+import io.opentelemetry.context.Scope;
+import io.opentelemetry.context.propagation.TextMapGetter;
+import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
+
 public class TraceUtil {
 
-  private static final Logger log = LoggerFactory.getLogger(TraceUtil.class);
-  public static final String TRACE_HOST_PROPERTY = "trace.host";
-  public static final String TRACE_SERVICE_PROPERTY = "trace.service";
-  public static final String TRACER_ZK_HOST = "tracer.zookeeper.host";
-  public static final String TRACER_ZK_TIMEOUT = "tracer.zookeeper.timeout";
-  public static final String TRACER_ZK_PATH = "tracer.zookeeper.path";
-  private static final HashSet<SpanReceiver> receivers = new HashSet<>();
+  public static final Logger LOG = LoggerFactory.getLogger(TraceUtil.class);
+
+  private static final String SPAN_FORMAT = "%s::%s";
+
+  private static Tracer instance = null;
+  private static String name = null;
+  private static boolean tracing = false;
+
+  private static void initializeInternals(OpenTelemetry ot, String 
instrumentationName) {
+    // TODO: Is there a way to get our version to pass to getTracer() ?
+    name = instrumentationName;
+    instance = ot.getTracer(name);
+    tracing = (!ot.equals(OpenTelemetry.noop()));
+    LOG.info("Trace enabled: {}, OpenTelemetry instance: {}, Tracer instance: 
{}", tracing,
+        ot.getClass(), instance.getClass());
+  }
 
   /**
-   * Enable tracing by setting up SpanReceivers for the current process. If 
host name is null, it
-   * will be determined. If service name is null, the simple name of the class 
will be used.
-   * Properties required in the client configuration include
-   * {@link org.apache.accumulo.core.conf.ClientProperty#TRACE_SPAN_RECEIVERS} 
and any properties
-   * specific to the span receiver.
+   * Initialize TracerUtil using the OpenTelemetry parameter and 
instrumentationName
+   *
+   * @param ot
+   *          OpenTelemetry instance
+   * @param instrumentationName
+   *          OpenTelemetry instrumentation library name
    */
-  public static void enableClientTraces(String hostname, String service, 
Properties properties) {
-    // @formatter:off
-      enableTracing(hostname, service,
-          ClientProperty.TRACE_SPAN_RECEIVERS.getValue(properties),
-          ClientProperty.INSTANCE_ZOOKEEPERS.getValue(properties),
-          ConfigurationTypeHelper
-              
.getTimeInMillis(ClientProperty.INSTANCE_ZOOKEEPERS_TIMEOUT.getValue(properties)),
-          ClientProperty.TRACE_ZOOKEEPER_PATH.getValue(properties),
-          ClientProperty.toMap(
-              ClientProperty.getPrefix(properties, 
ClientProperty.TRACE_SPAN_RECEIVER_PREFIX)));
-      // @formatter:on
+  public static void initializeTracer(OpenTelemetry ot, String 
instrumentationName) {
+    if (instance != null) {
+      initializeInternals(ot, instrumentationName);
+    } else {
+      LOG.warn("Tracer already initialized.");
+    }
   }
 
   /**
-   * Enable tracing by setting up SpanReceivers for the current process. If 
host name is null, it
-   * will be determined. If service name is null, the simple name of the class 
will be used.
+   * Use the property values in the AccumuloConfiguration to call
+   * {@link #initializeTracer(boolean, String, String)}
+   *
+   * @param conf
+   *          AccumuloConfiguration
+   * @throws Exception
+   *           unable to find or load class
    */
-  public static void enableServerTraces(String hostname, String service,
-      AccumuloConfiguration conf) {
-    // @formatter:off
-      enableTracing(hostname, service,
-          conf.get(Property.TRACE_SPAN_RECEIVERS),
-          conf.get(Property.INSTANCE_ZK_HOST),
-          conf.getTimeInMillis(Property.INSTANCE_ZK_TIMEOUT),
-          conf.get(Property.TRACE_ZK_PATH),
-          
conf.getAllPropertiesWithPrefix(Property.TRACE_SPAN_RECEIVER_PREFIX));
-      // @formatter:on
+  public static void initializeTracer(AccumuloConfiguration conf) throws 
Exception {
+    initializeTracer(conf.getBoolean(Property.GENERAL_OPENTELEMETRY_ENABLED),
+        conf.get(Property.GENERAL_OPENTELEMETRY_FACTORY),
+        conf.get(Property.GENERAL_OPENTELEMETRY_NAME));
   }
 
-  private static void enableTracing(String hostname, String service, String 
spanReceivers,
-      String zookeepers, long timeout, String zkPath, Map<String,String> 
spanReceiverProps) {
-
-    Map<String,
-        String> htraceConfigProps = 
spanReceiverProps.entrySet().stream().collect(Collectors.toMap(
-            k -> 
String.valueOf(k).substring(Property.TRACE_SPAN_RECEIVER_PREFIX.getKey().length()),
-            String::valueOf, (a, b) -> {
-              throw new AssertionError("duplicate can't happen");
-            }, HashMap::new));
-    htraceConfigProps.put(TRACER_ZK_HOST, zookeepers);
-    htraceConfigProps.put(TRACER_ZK_TIMEOUT, Long.toString(timeout));
-    htraceConfigProps.put(TRACER_ZK_PATH, zkPath);
-    if (hostname != null) {
-      htraceConfigProps.put(TRACE_HOST_PROPERTY, hostname);
-    }
-    if (service != null) {
-      htraceConfigProps.put(TRACE_SERVICE_PROPERTY, service);
-    }
-    ShutdownHookManager.get().addShutdownHook(TraceUtil::disable, 0);
-    synchronized (receivers) {
-      if (!receivers.isEmpty()) {
-        log.info("Already loaded span receivers, enable tracing does not need 
to be called again");
-        return;
-      }
-      if (spanReceivers == null) {
-        return;
-      }
-      Stream.of(spanReceivers.split(",")).map(String::trim).filter(s -> 
!s.isEmpty())
-          .forEach(className -> {
-            HTraceConfiguration htraceConf = 
HTraceConfiguration.fromMap(htraceConfigProps);
-            SpanReceiverBuilder builder = new SpanReceiverBuilder(htraceConf);
-            SpanReceiver rcvr = 
builder.spanReceiverClass(className.trim()).build();
-            if (rcvr == null) {
-              log.warn("Failed to load SpanReceiver {}", className);
-            } else {
-              receivers.add(rcvr);
-              log.debug("SpanReceiver {} was loaded successfully.", className);
-            }
-          });
-      for (SpanReceiver rcvr : receivers) {
-        Trace.addReceiver(rcvr);
+  /**
+   * If not enabled, the OpenTelemetry implementation will be set to the NoOp 
implementation. If
+   * enabled and a factoryClass is supplied, then we will get the 
OpenTelemetry instance from the
+   * factory class.
+   *
+   * @param enabled
+   *          whether or not tracing is enabled
+   * @param factoryClass
+   *          name of class to load
+   * @param instrumentationName
+   *          OpenTelemetry instrumentation library name
+   * @throws Exception
+   *           unable to find or load class
+   */
+  public static void initializeTracer(boolean enabled, String factoryClass,
+      String instrumentationName) throws Exception {
+    if (instance == null) {
+      OpenTelemetry ot = null;
+      if (!enabled) {
+        ot = OpenTelemetry.noop();
+      } else if (factoryClass != null && !factoryClass.isEmpty()) {
+        Class<? extends OpenTelemetryFactory> clazz =
+            (Class<? extends OpenTelemetryFactory>) 
ClassLoaderUtil.loadClass(factoryClass,
+                OpenTelemetryFactory.class);
+        OpenTelemetryFactory factory = 
clazz.getDeclaredConstructor().newInstance();
+        ot = factory.getOpenTelemetry();
+        LOG.info("OpenTelemetry configured and set from {}", clazz);
+      } else {
+        ot = GlobalOpenTelemetry.get();
       }
+      initializeInternals(ot, instrumentationName);
+    } else {
+      LOG.warn("Tracer already initialized.");
     }
   }
 
   /**
-   * Disable tracing by closing SpanReceivers for the current process.
+   * @return the Tracer set on the GlobalOpenTelemetry object
    */
-  public static void disable() {
-    synchronized (receivers) {
-      receivers.forEach(rcvr -> {
-        try {
-          rcvr.close();
-        } catch (IOException e) {
-          log.warn("Unable to close SpanReceiver correctly: {}", 
e.getMessage(), e);
-        }
-      });
-      receivers.clear();
+  private static Tracer getTracer() {
+    if (Objects.isNull(instance)) {
+      LOG.warn("initializeTracer not called, using 
GlobalOpenTelemetry.getTracer()");
+      instance = GlobalOpenTelemetry.getTracer(name);
+      tracing = (!instance.equals(OpenTelemetry.noop().getTracer(name)));
+      LOG.info("Trace enabled: {}, Tracer is: {}", tracing, 
instance.getClass());
     }
+    return instance;
   }
 
   /**
-   * Continue a trace by starting a new span with a given parent and 
description.
+   * @return true if an OpenTelemetry Tracer implementation has been set, 
false if the NoOp Tracer
+   *         is being used.
    */
-  public static TraceScope trace(TInfo info, String description) {
-    return info.traceId == 0 ? NullScope.INSTANCE
-        : Trace.startSpan(description, new TraceInfo(info.traceId, 
info.parentId));
+  public static boolean isTracing() {
+    return tracing;
   }
 
-  private static final TInfo DONT_TRACE = new TInfo(0, 0);
+  public static Span createSpan(Class<?> caller, String spanName, SpanKind 
kind) {
+    return createSpan(caller, spanName, kind, null, false, null);
+  }
 
-  /**
-   * Obtain {@link org.apache.accumulo.core.trace.thrift.TInfo} for the 
current span.
-   */
-  public static TInfo traceInfo() {
-    Span span = Trace.currentSpan();
-    if (span != null) {
-      return new TInfo(span.getTraceId(), span.getSpanId());
+  public static Span createSpan(Class<?> caller, String spanName, SpanKind 
kind, Context parent) {
+    return createSpan(caller, spanName, kind, null, false, parent);
+  }
+
+  public static Span createSpan(Class<?> caller, String spanName, SpanKind 
kind,
+      Map<String,String> attributes) {
+    return createSpan(caller, spanName, kind, attributes, false, null);
+  }
+
+  public static Span createSpan(Class<?> caller, String spanName, SpanKind 
kind,
+      Map<String,String> attributes, boolean setNoParent, Context parent) {
+    final String name = String.format(SPAN_FORMAT, caller.getSimpleName(), 
spanName);
+    final SpanBuilder builder = getTracer().spanBuilder(name);
+    builder.setSpanKind(kind);
+    if (attributes != null) {
+      attributes.forEach((k, v) -> builder.setAttribute(k, v));

Review comment:
       ```suggestion
         attributes.forEach(builder::setAttribute);
   ```

##########
File path: core/src/main/java/org/apache/accumulo/core/trace/TraceUtil.java
##########
@@ -18,211 +18,230 @@
  */
 package org.apache.accumulo.core.trace;
 
-import java.io.IOException;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Proxy;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Map;
-import java.util.Properties;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
+import java.util.Objects;
 
+import org.apache.accumulo.core.classloader.ClassLoaderUtil;
 import org.apache.accumulo.core.conf.AccumuloConfiguration;
-import org.apache.accumulo.core.conf.ClientProperty;
-import org.apache.accumulo.core.conf.ConfigurationTypeHelper;
 import org.apache.accumulo.core.conf.Property;
+import org.apache.accumulo.core.spi.trace.OpenTelemetryFactory;
 import org.apache.accumulo.core.trace.thrift.TInfo;
-import org.apache.hadoop.util.ShutdownHookManager;
-import org.apache.htrace.HTraceConfiguration;
-import org.apache.htrace.NullScope;
-import org.apache.htrace.Span;
-import org.apache.htrace.SpanReceiver;
-import org.apache.htrace.SpanReceiverBuilder;
-import org.apache.htrace.Trace;
-import org.apache.htrace.TraceInfo;
-import org.apache.htrace.TraceScope;
-import org.apache.htrace.impl.CountSampler;
-import org.apache.htrace.impl.ProbabilitySampler;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-/**
- * Utility class for tracing within Accumulo. Not intended for client use!
- */
+import io.opentelemetry.api.GlobalOpenTelemetry;
+import io.opentelemetry.api.OpenTelemetry;
+import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.trace.Span;
+import io.opentelemetry.api.trace.SpanBuilder;
+import io.opentelemetry.api.trace.SpanKind;
+import io.opentelemetry.api.trace.StatusCode;
+import io.opentelemetry.api.trace.Tracer;
+import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
+import io.opentelemetry.context.Context;
+import io.opentelemetry.context.Scope;
+import io.opentelemetry.context.propagation.TextMapGetter;
+import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
+
 public class TraceUtil {
 
-  private static final Logger log = LoggerFactory.getLogger(TraceUtil.class);
-  public static final String TRACE_HOST_PROPERTY = "trace.host";
-  public static final String TRACE_SERVICE_PROPERTY = "trace.service";
-  public static final String TRACER_ZK_HOST = "tracer.zookeeper.host";
-  public static final String TRACER_ZK_TIMEOUT = "tracer.zookeeper.timeout";
-  public static final String TRACER_ZK_PATH = "tracer.zookeeper.path";
-  private static final HashSet<SpanReceiver> receivers = new HashSet<>();
+  public static final Logger LOG = LoggerFactory.getLogger(TraceUtil.class);
+
+  private static final String SPAN_FORMAT = "%s::%s";
+
+  private static Tracer instance = null;
+  private static String name = null;
+  private static boolean tracing = false;
+
+  private static void initializeInternals(OpenTelemetry ot, String 
instrumentationName) {
+    // TODO: Is there a way to get our version to pass to getTracer() ?
+    name = instrumentationName;
+    instance = ot.getTracer(name);
+    tracing = (!ot.equals(OpenTelemetry.noop()));
+    LOG.info("Trace enabled: {}, OpenTelemetry instance: {}, Tracer instance: 
{}", tracing,
+        ot.getClass(), instance.getClass());
+  }
 
   /**
-   * Enable tracing by setting up SpanReceivers for the current process. If 
host name is null, it
-   * will be determined. If service name is null, the simple name of the class 
will be used.
-   * Properties required in the client configuration include
-   * {@link org.apache.accumulo.core.conf.ClientProperty#TRACE_SPAN_RECEIVERS} 
and any properties
-   * specific to the span receiver.
+   * Initialize TracerUtil using the OpenTelemetry parameter and 
instrumentationName
+   *
+   * @param ot
+   *          OpenTelemetry instance
+   * @param instrumentationName
+   *          OpenTelemetry instrumentation library name
    */
-  public static void enableClientTraces(String hostname, String service, 
Properties properties) {
-    // @formatter:off
-      enableTracing(hostname, service,
-          ClientProperty.TRACE_SPAN_RECEIVERS.getValue(properties),
-          ClientProperty.INSTANCE_ZOOKEEPERS.getValue(properties),
-          ConfigurationTypeHelper
-              
.getTimeInMillis(ClientProperty.INSTANCE_ZOOKEEPERS_TIMEOUT.getValue(properties)),
-          ClientProperty.TRACE_ZOOKEEPER_PATH.getValue(properties),
-          ClientProperty.toMap(
-              ClientProperty.getPrefix(properties, 
ClientProperty.TRACE_SPAN_RECEIVER_PREFIX)));
-      // @formatter:on
+  public static void initializeTracer(OpenTelemetry ot, String 
instrumentationName) {
+    if (instance != null) {
+      initializeInternals(ot, instrumentationName);
+    } else {
+      LOG.warn("Tracer already initialized.");
+    }
   }
 
   /**
-   * Enable tracing by setting up SpanReceivers for the current process. If 
host name is null, it
-   * will be determined. If service name is null, the simple name of the class 
will be used.
+   * Use the property values in the AccumuloConfiguration to call
+   * {@link #initializeTracer(boolean, String, String)}
+   *
+   * @param conf
+   *          AccumuloConfiguration
+   * @throws Exception
+   *           unable to find or load class
    */
-  public static void enableServerTraces(String hostname, String service,
-      AccumuloConfiguration conf) {
-    // @formatter:off
-      enableTracing(hostname, service,
-          conf.get(Property.TRACE_SPAN_RECEIVERS),
-          conf.get(Property.INSTANCE_ZK_HOST),
-          conf.getTimeInMillis(Property.INSTANCE_ZK_TIMEOUT),
-          conf.get(Property.TRACE_ZK_PATH),
-          
conf.getAllPropertiesWithPrefix(Property.TRACE_SPAN_RECEIVER_PREFIX));
-      // @formatter:on
+  public static void initializeTracer(AccumuloConfiguration conf) throws 
Exception {
+    initializeTracer(conf.getBoolean(Property.GENERAL_OPENTELEMETRY_ENABLED),
+        conf.get(Property.GENERAL_OPENTELEMETRY_FACTORY),
+        conf.get(Property.GENERAL_OPENTELEMETRY_NAME));
   }
 
-  private static void enableTracing(String hostname, String service, String 
spanReceivers,
-      String zookeepers, long timeout, String zkPath, Map<String,String> 
spanReceiverProps) {
-
-    Map<String,
-        String> htraceConfigProps = 
spanReceiverProps.entrySet().stream().collect(Collectors.toMap(
-            k -> 
String.valueOf(k).substring(Property.TRACE_SPAN_RECEIVER_PREFIX.getKey().length()),
-            String::valueOf, (a, b) -> {
-              throw new AssertionError("duplicate can't happen");
-            }, HashMap::new));
-    htraceConfigProps.put(TRACER_ZK_HOST, zookeepers);
-    htraceConfigProps.put(TRACER_ZK_TIMEOUT, Long.toString(timeout));
-    htraceConfigProps.put(TRACER_ZK_PATH, zkPath);
-    if (hostname != null) {
-      htraceConfigProps.put(TRACE_HOST_PROPERTY, hostname);
-    }
-    if (service != null) {
-      htraceConfigProps.put(TRACE_SERVICE_PROPERTY, service);
-    }
-    ShutdownHookManager.get().addShutdownHook(TraceUtil::disable, 0);
-    synchronized (receivers) {
-      if (!receivers.isEmpty()) {
-        log.info("Already loaded span receivers, enable tracing does not need 
to be called again");
-        return;
-      }
-      if (spanReceivers == null) {
-        return;
-      }
-      Stream.of(spanReceivers.split(",")).map(String::trim).filter(s -> 
!s.isEmpty())
-          .forEach(className -> {
-            HTraceConfiguration htraceConf = 
HTraceConfiguration.fromMap(htraceConfigProps);
-            SpanReceiverBuilder builder = new SpanReceiverBuilder(htraceConf);
-            SpanReceiver rcvr = 
builder.spanReceiverClass(className.trim()).build();
-            if (rcvr == null) {
-              log.warn("Failed to load SpanReceiver {}", className);
-            } else {
-              receivers.add(rcvr);
-              log.debug("SpanReceiver {} was loaded successfully.", className);
-            }
-          });
-      for (SpanReceiver rcvr : receivers) {
-        Trace.addReceiver(rcvr);
+  /**
+   * If not enabled, the OpenTelemetry implementation will be set to the NoOp 
implementation. If
+   * enabled and a factoryClass is supplied, then we will get the 
OpenTelemetry instance from the
+   * factory class.
+   *
+   * @param enabled
+   *          whether or not tracing is enabled
+   * @param factoryClass
+   *          name of class to load
+   * @param instrumentationName
+   *          OpenTelemetry instrumentation library name
+   * @throws Exception
+   *           unable to find or load class
+   */
+  public static void initializeTracer(boolean enabled, String factoryClass,
+      String instrumentationName) throws Exception {
+    if (instance == null) {
+      OpenTelemetry ot = null;
+      if (!enabled) {
+        ot = OpenTelemetry.noop();
+      } else if (factoryClass != null && !factoryClass.isEmpty()) {
+        Class<? extends OpenTelemetryFactory> clazz =
+            (Class<? extends OpenTelemetryFactory>) 
ClassLoaderUtil.loadClass(factoryClass,
+                OpenTelemetryFactory.class);
+        OpenTelemetryFactory factory = 
clazz.getDeclaredConstructor().newInstance();
+        ot = factory.getOpenTelemetry();
+        LOG.info("OpenTelemetry configured and set from {}", clazz);
+      } else {
+        ot = GlobalOpenTelemetry.get();
       }
+      initializeInternals(ot, instrumentationName);
+    } else {
+      LOG.warn("Tracer already initialized.");
     }
   }
 
   /**
-   * Disable tracing by closing SpanReceivers for the current process.
+   * @return the Tracer set on the GlobalOpenTelemetry object
    */
-  public static void disable() {
-    synchronized (receivers) {
-      receivers.forEach(rcvr -> {
-        try {
-          rcvr.close();
-        } catch (IOException e) {
-          log.warn("Unable to close SpanReceiver correctly: {}", 
e.getMessage(), e);
-        }
-      });
-      receivers.clear();
+  private static Tracer getTracer() {
+    if (Objects.isNull(instance)) {
+      LOG.warn("initializeTracer not called, using 
GlobalOpenTelemetry.getTracer()");
+      instance = GlobalOpenTelemetry.getTracer(name);
+      tracing = (!instance.equals(OpenTelemetry.noop().getTracer(name)));

Review comment:
       Extra parens makes it harder to parse visually
   ```suggestion
         tracing = !instance.equals(OpenTelemetry.noop().getTracer(name));
   ```

##########
File path: core/src/main/java/org/apache/accumulo/core/trace/TraceUtil.java
##########
@@ -18,211 +18,230 @@
  */
 package org.apache.accumulo.core.trace;
 
-import java.io.IOException;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Proxy;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Map;
-import java.util.Properties;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
+import java.util.Objects;
 
+import org.apache.accumulo.core.classloader.ClassLoaderUtil;
 import org.apache.accumulo.core.conf.AccumuloConfiguration;
-import org.apache.accumulo.core.conf.ClientProperty;
-import org.apache.accumulo.core.conf.ConfigurationTypeHelper;
 import org.apache.accumulo.core.conf.Property;
+import org.apache.accumulo.core.spi.trace.OpenTelemetryFactory;
 import org.apache.accumulo.core.trace.thrift.TInfo;
-import org.apache.hadoop.util.ShutdownHookManager;
-import org.apache.htrace.HTraceConfiguration;
-import org.apache.htrace.NullScope;
-import org.apache.htrace.Span;
-import org.apache.htrace.SpanReceiver;
-import org.apache.htrace.SpanReceiverBuilder;
-import org.apache.htrace.Trace;
-import org.apache.htrace.TraceInfo;
-import org.apache.htrace.TraceScope;
-import org.apache.htrace.impl.CountSampler;
-import org.apache.htrace.impl.ProbabilitySampler;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-/**
- * Utility class for tracing within Accumulo. Not intended for client use!
- */
+import io.opentelemetry.api.GlobalOpenTelemetry;
+import io.opentelemetry.api.OpenTelemetry;
+import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.trace.Span;
+import io.opentelemetry.api.trace.SpanBuilder;
+import io.opentelemetry.api.trace.SpanKind;
+import io.opentelemetry.api.trace.StatusCode;
+import io.opentelemetry.api.trace.Tracer;
+import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
+import io.opentelemetry.context.Context;
+import io.opentelemetry.context.Scope;
+import io.opentelemetry.context.propagation.TextMapGetter;
+import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
+
 public class TraceUtil {
 
-  private static final Logger log = LoggerFactory.getLogger(TraceUtil.class);
-  public static final String TRACE_HOST_PROPERTY = "trace.host";
-  public static final String TRACE_SERVICE_PROPERTY = "trace.service";
-  public static final String TRACER_ZK_HOST = "tracer.zookeeper.host";
-  public static final String TRACER_ZK_TIMEOUT = "tracer.zookeeper.timeout";
-  public static final String TRACER_ZK_PATH = "tracer.zookeeper.path";
-  private static final HashSet<SpanReceiver> receivers = new HashSet<>();
+  public static final Logger LOG = LoggerFactory.getLogger(TraceUtil.class);
+
+  private static final String SPAN_FORMAT = "%s::%s";
+
+  private static Tracer instance = null;
+  private static String name = null;
+  private static boolean tracing = false;
+
+  private static void initializeInternals(OpenTelemetry ot, String 
instrumentationName) {
+    // TODO: Is there a way to get our version to pass to getTracer() ?
+    name = instrumentationName;
+    instance = ot.getTracer(name);
+    tracing = (!ot.equals(OpenTelemetry.noop()));
+    LOG.info("Trace enabled: {}, OpenTelemetry instance: {}, Tracer instance: 
{}", tracing,
+        ot.getClass(), instance.getClass());
+  }
 
   /**
-   * Enable tracing by setting up SpanReceivers for the current process. If 
host name is null, it
-   * will be determined. If service name is null, the simple name of the class 
will be used.
-   * Properties required in the client configuration include
-   * {@link org.apache.accumulo.core.conf.ClientProperty#TRACE_SPAN_RECEIVERS} 
and any properties
-   * specific to the span receiver.
+   * Initialize TracerUtil using the OpenTelemetry parameter and 
instrumentationName
+   *
+   * @param ot
+   *          OpenTelemetry instance
+   * @param instrumentationName
+   *          OpenTelemetry instrumentation library name
    */
-  public static void enableClientTraces(String hostname, String service, 
Properties properties) {
-    // @formatter:off
-      enableTracing(hostname, service,
-          ClientProperty.TRACE_SPAN_RECEIVERS.getValue(properties),
-          ClientProperty.INSTANCE_ZOOKEEPERS.getValue(properties),
-          ConfigurationTypeHelper
-              
.getTimeInMillis(ClientProperty.INSTANCE_ZOOKEEPERS_TIMEOUT.getValue(properties)),
-          ClientProperty.TRACE_ZOOKEEPER_PATH.getValue(properties),
-          ClientProperty.toMap(
-              ClientProperty.getPrefix(properties, 
ClientProperty.TRACE_SPAN_RECEIVER_PREFIX)));
-      // @formatter:on
+  public static void initializeTracer(OpenTelemetry ot, String 
instrumentationName) {
+    if (instance != null) {
+      initializeInternals(ot, instrumentationName);
+    } else {
+      LOG.warn("Tracer already initialized.");
+    }
   }
 
   /**
-   * Enable tracing by setting up SpanReceivers for the current process. If 
host name is null, it
-   * will be determined. If service name is null, the simple name of the class 
will be used.
+   * Use the property values in the AccumuloConfiguration to call
+   * {@link #initializeTracer(boolean, String, String)}
+   *
+   * @param conf
+   *          AccumuloConfiguration
+   * @throws Exception
+   *           unable to find or load class
    */
-  public static void enableServerTraces(String hostname, String service,
-      AccumuloConfiguration conf) {
-    // @formatter:off
-      enableTracing(hostname, service,
-          conf.get(Property.TRACE_SPAN_RECEIVERS),
-          conf.get(Property.INSTANCE_ZK_HOST),
-          conf.getTimeInMillis(Property.INSTANCE_ZK_TIMEOUT),
-          conf.get(Property.TRACE_ZK_PATH),
-          
conf.getAllPropertiesWithPrefix(Property.TRACE_SPAN_RECEIVER_PREFIX));
-      // @formatter:on
+  public static void initializeTracer(AccumuloConfiguration conf) throws 
Exception {
+    initializeTracer(conf.getBoolean(Property.GENERAL_OPENTELEMETRY_ENABLED),
+        conf.get(Property.GENERAL_OPENTELEMETRY_FACTORY),
+        conf.get(Property.GENERAL_OPENTELEMETRY_NAME));
   }
 
-  private static void enableTracing(String hostname, String service, String 
spanReceivers,
-      String zookeepers, long timeout, String zkPath, Map<String,String> 
spanReceiverProps) {
-
-    Map<String,
-        String> htraceConfigProps = 
spanReceiverProps.entrySet().stream().collect(Collectors.toMap(
-            k -> 
String.valueOf(k).substring(Property.TRACE_SPAN_RECEIVER_PREFIX.getKey().length()),
-            String::valueOf, (a, b) -> {
-              throw new AssertionError("duplicate can't happen");
-            }, HashMap::new));
-    htraceConfigProps.put(TRACER_ZK_HOST, zookeepers);
-    htraceConfigProps.put(TRACER_ZK_TIMEOUT, Long.toString(timeout));
-    htraceConfigProps.put(TRACER_ZK_PATH, zkPath);
-    if (hostname != null) {
-      htraceConfigProps.put(TRACE_HOST_PROPERTY, hostname);
-    }
-    if (service != null) {
-      htraceConfigProps.put(TRACE_SERVICE_PROPERTY, service);
-    }
-    ShutdownHookManager.get().addShutdownHook(TraceUtil::disable, 0);
-    synchronized (receivers) {
-      if (!receivers.isEmpty()) {
-        log.info("Already loaded span receivers, enable tracing does not need 
to be called again");
-        return;
-      }
-      if (spanReceivers == null) {
-        return;
-      }
-      Stream.of(spanReceivers.split(",")).map(String::trim).filter(s -> 
!s.isEmpty())
-          .forEach(className -> {
-            HTraceConfiguration htraceConf = 
HTraceConfiguration.fromMap(htraceConfigProps);
-            SpanReceiverBuilder builder = new SpanReceiverBuilder(htraceConf);
-            SpanReceiver rcvr = 
builder.spanReceiverClass(className.trim()).build();
-            if (rcvr == null) {
-              log.warn("Failed to load SpanReceiver {}", className);
-            } else {
-              receivers.add(rcvr);
-              log.debug("SpanReceiver {} was loaded successfully.", className);
-            }
-          });
-      for (SpanReceiver rcvr : receivers) {
-        Trace.addReceiver(rcvr);
+  /**
+   * If not enabled, the OpenTelemetry implementation will be set to the NoOp 
implementation. If
+   * enabled and a factoryClass is supplied, then we will get the 
OpenTelemetry instance from the
+   * factory class.
+   *
+   * @param enabled
+   *          whether or not tracing is enabled
+   * @param factoryClass
+   *          name of class to load
+   * @param instrumentationName
+   *          OpenTelemetry instrumentation library name
+   * @throws Exception
+   *           unable to find or load class
+   */
+  public static void initializeTracer(boolean enabled, String factoryClass,
+      String instrumentationName) throws Exception {
+    if (instance == null) {
+      OpenTelemetry ot = null;
+      if (!enabled) {
+        ot = OpenTelemetry.noop();
+      } else if (factoryClass != null && !factoryClass.isEmpty()) {
+        Class<? extends OpenTelemetryFactory> clazz =
+            (Class<? extends OpenTelemetryFactory>) 
ClassLoaderUtil.loadClass(factoryClass,
+                OpenTelemetryFactory.class);
+        OpenTelemetryFactory factory = 
clazz.getDeclaredConstructor().newInstance();
+        ot = factory.getOpenTelemetry();
+        LOG.info("OpenTelemetry configured and set from {}", clazz);
+      } else {
+        ot = GlobalOpenTelemetry.get();
       }
+      initializeInternals(ot, instrumentationName);
+    } else {
+      LOG.warn("Tracer already initialized.");
     }
   }
 
   /**
-   * Disable tracing by closing SpanReceivers for the current process.
+   * @return the Tracer set on the GlobalOpenTelemetry object
    */
-  public static void disable() {
-    synchronized (receivers) {
-      receivers.forEach(rcvr -> {
-        try {
-          rcvr.close();
-        } catch (IOException e) {
-          log.warn("Unable to close SpanReceiver correctly: {}", 
e.getMessage(), e);
-        }
-      });
-      receivers.clear();
+  private static Tracer getTracer() {
+    if (Objects.isNull(instance)) {
+      LOG.warn("initializeTracer not called, using 
GlobalOpenTelemetry.getTracer()");
+      instance = GlobalOpenTelemetry.getTracer(name);
+      tracing = (!instance.equals(OpenTelemetry.noop().getTracer(name)));
+      LOG.info("Trace enabled: {}, Tracer is: {}", tracing, 
instance.getClass());
     }
+    return instance;
   }
 
   /**
-   * Continue a trace by starting a new span with a given parent and 
description.
+   * @return true if an OpenTelemetry Tracer implementation has been set, 
false if the NoOp Tracer
+   *         is being used.
    */
-  public static TraceScope trace(TInfo info, String description) {
-    return info.traceId == 0 ? NullScope.INSTANCE
-        : Trace.startSpan(description, new TraceInfo(info.traceId, 
info.parentId));
+  public static boolean isTracing() {
+    return tracing;
   }
 
-  private static final TInfo DONT_TRACE = new TInfo(0, 0);
+  public static Span createSpan(Class<?> caller, String spanName, SpanKind 
kind) {
+    return createSpan(caller, spanName, kind, null, false, null);
+  }
 
-  /**
-   * Obtain {@link org.apache.accumulo.core.trace.thrift.TInfo} for the 
current span.
-   */
-  public static TInfo traceInfo() {
-    Span span = Trace.currentSpan();
-    if (span != null) {
-      return new TInfo(span.getTraceId(), span.getSpanId());
+  public static Span createSpan(Class<?> caller, String spanName, SpanKind 
kind, Context parent) {
+    return createSpan(caller, spanName, kind, null, false, parent);
+  }
+
+  public static Span createSpan(Class<?> caller, String spanName, SpanKind 
kind,
+      Map<String,String> attributes) {
+    return createSpan(caller, spanName, kind, attributes, false, null);
+  }
+
+  public static Span createSpan(Class<?> caller, String spanName, SpanKind 
kind,
+      Map<String,String> attributes, boolean setNoParent, Context parent) {

Review comment:
       `setNoParent` and `parent` seem to be performing an overlapping role. 
Can `setNoParent` always be inferred from `parent` being `null`?

##########
File path: 
core/src/test/java/org/apache/accumulo/core/conf/cluster/ClusterConfigParserTest.java
##########
@@ -75,13 +74,12 @@ public void testParseWithExternalCompactions() throws 
Exception {
 
     Map<String,String> contents =
         ClusterConfigParser.parseConfiguration(new 
File(configFile.toURI()).getAbsolutePath());
-    assertEquals(9, contents.size());
+    assertEquals(8, contents.size());
     assertTrue(contents.containsKey("manager"));
     assertEquals("localhost1 localhost2", contents.get("manager"));
     assertTrue(contents.containsKey("monitor"));
     assertEquals("localhost1 localhost2", contents.get("monitor"));
-    assertTrue(contents.containsKey("tracer"));
-    assertEquals("localhost", contents.get("tracer"));
+    assertFalse(contents.containsKey("tracer"));

Review comment:
       ```suggestion
   ```

##########
File path: core/src/main/java/org/apache/accumulo/core/trace/TraceUtil.java
##########
@@ -231,8 +250,16 @@ public static ProbabilitySampler probabilitySampler(double 
fraction) {
         if (args == null || args.length < 1 || args[0] == null || !(args[0] 
instanceof TInfo)) {
           return method.invoke(instance, args);
         }
-        try (TraceScope span = trace((TInfo) args[0], method.getName())) {
+        TInfo tinfo = (TInfo) args[0];
+        getContext(tinfo).makeCurrent();
+        Span span = createSpan(instance.getClass(), method.getName(), 
SpanKind.SERVER);
+        try (Scope scope = span.makeCurrent()) {

Review comment:
       I'm not sure this is the right pattern. I don't think we want to 
`makeCurrent` on the parent, because the Closeable resource object it returns 
is never closed. Instead, I think we just want to set the parent on the Span we 
create:
   
   ```suggestion
           Context parent = getContext((TInfo) args[0]);
           Span span = createSpan(instance.getClass(), method.getName(), 
SpanKind.SERVER, parent);
           try (Scope scope = span.makeCurrent()) {
   ```

##########
File path: core/src/main/java/org/apache/accumulo/core/trace/TraceUtil.java
##########
@@ -18,211 +18,230 @@
  */
 package org.apache.accumulo.core.trace;
 
-import java.io.IOException;
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Proxy;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Map;
-import java.util.Properties;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
+import java.util.Objects;
 
+import org.apache.accumulo.core.classloader.ClassLoaderUtil;
 import org.apache.accumulo.core.conf.AccumuloConfiguration;
-import org.apache.accumulo.core.conf.ClientProperty;
-import org.apache.accumulo.core.conf.ConfigurationTypeHelper;
 import org.apache.accumulo.core.conf.Property;
+import org.apache.accumulo.core.spi.trace.OpenTelemetryFactory;
 import org.apache.accumulo.core.trace.thrift.TInfo;
-import org.apache.hadoop.util.ShutdownHookManager;
-import org.apache.htrace.HTraceConfiguration;
-import org.apache.htrace.NullScope;
-import org.apache.htrace.Span;
-import org.apache.htrace.SpanReceiver;
-import org.apache.htrace.SpanReceiverBuilder;
-import org.apache.htrace.Trace;
-import org.apache.htrace.TraceInfo;
-import org.apache.htrace.TraceScope;
-import org.apache.htrace.impl.CountSampler;
-import org.apache.htrace.impl.ProbabilitySampler;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-/**
- * Utility class for tracing within Accumulo. Not intended for client use!
- */
+import io.opentelemetry.api.GlobalOpenTelemetry;
+import io.opentelemetry.api.OpenTelemetry;
+import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.trace.Span;
+import io.opentelemetry.api.trace.SpanBuilder;
+import io.opentelemetry.api.trace.SpanKind;
+import io.opentelemetry.api.trace.StatusCode;
+import io.opentelemetry.api.trace.Tracer;
+import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;
+import io.opentelemetry.context.Context;
+import io.opentelemetry.context.Scope;
+import io.opentelemetry.context.propagation.TextMapGetter;
+import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
+
 public class TraceUtil {
 
-  private static final Logger log = LoggerFactory.getLogger(TraceUtil.class);
-  public static final String TRACE_HOST_PROPERTY = "trace.host";
-  public static final String TRACE_SERVICE_PROPERTY = "trace.service";
-  public static final String TRACER_ZK_HOST = "tracer.zookeeper.host";
-  public static final String TRACER_ZK_TIMEOUT = "tracer.zookeeper.timeout";
-  public static final String TRACER_ZK_PATH = "tracer.zookeeper.path";
-  private static final HashSet<SpanReceiver> receivers = new HashSet<>();
+  public static final Logger LOG = LoggerFactory.getLogger(TraceUtil.class);
+
+  private static final String SPAN_FORMAT = "%s::%s";
+
+  private static Tracer instance = null;
+  private static String name = null;
+  private static boolean tracing = false;
+
+  private static void initializeInternals(OpenTelemetry ot, String 
instrumentationName) {
+    // TODO: Is there a way to get our version to pass to getTracer() ?
+    name = instrumentationName;
+    instance = ot.getTracer(name);
+    tracing = (!ot.equals(OpenTelemetry.noop()));
+    LOG.info("Trace enabled: {}, OpenTelemetry instance: {}, Tracer instance: 
{}", tracing,
+        ot.getClass(), instance.getClass());
+  }
 
   /**
-   * Enable tracing by setting up SpanReceivers for the current process. If 
host name is null, it
-   * will be determined. If service name is null, the simple name of the class 
will be used.
-   * Properties required in the client configuration include
-   * {@link org.apache.accumulo.core.conf.ClientProperty#TRACE_SPAN_RECEIVERS} 
and any properties
-   * specific to the span receiver.
+   * Initialize TracerUtil using the OpenTelemetry parameter and 
instrumentationName
+   *
+   * @param ot
+   *          OpenTelemetry instance
+   * @param instrumentationName
+   *          OpenTelemetry instrumentation library name
    */
-  public static void enableClientTraces(String hostname, String service, 
Properties properties) {
-    // @formatter:off
-      enableTracing(hostname, service,
-          ClientProperty.TRACE_SPAN_RECEIVERS.getValue(properties),
-          ClientProperty.INSTANCE_ZOOKEEPERS.getValue(properties),
-          ConfigurationTypeHelper
-              
.getTimeInMillis(ClientProperty.INSTANCE_ZOOKEEPERS_TIMEOUT.getValue(properties)),
-          ClientProperty.TRACE_ZOOKEEPER_PATH.getValue(properties),
-          ClientProperty.toMap(
-              ClientProperty.getPrefix(properties, 
ClientProperty.TRACE_SPAN_RECEIVER_PREFIX)));
-      // @formatter:on
+  public static void initializeTracer(OpenTelemetry ot, String 
instrumentationName) {
+    if (instance != null) {
+      initializeInternals(ot, instrumentationName);
+    } else {
+      LOG.warn("Tracer already initialized.");
+    }
   }
 
   /**
-   * Enable tracing by setting up SpanReceivers for the current process. If 
host name is null, it
-   * will be determined. If service name is null, the simple name of the class 
will be used.
+   * Use the property values in the AccumuloConfiguration to call
+   * {@link #initializeTracer(boolean, String, String)}
+   *
+   * @param conf
+   *          AccumuloConfiguration
+   * @throws Exception
+   *           unable to find or load class
    */
-  public static void enableServerTraces(String hostname, String service,
-      AccumuloConfiguration conf) {
-    // @formatter:off
-      enableTracing(hostname, service,
-          conf.get(Property.TRACE_SPAN_RECEIVERS),
-          conf.get(Property.INSTANCE_ZK_HOST),
-          conf.getTimeInMillis(Property.INSTANCE_ZK_TIMEOUT),
-          conf.get(Property.TRACE_ZK_PATH),
-          
conf.getAllPropertiesWithPrefix(Property.TRACE_SPAN_RECEIVER_PREFIX));
-      // @formatter:on
+  public static void initializeTracer(AccumuloConfiguration conf) throws 
Exception {
+    initializeTracer(conf.getBoolean(Property.GENERAL_OPENTELEMETRY_ENABLED),
+        conf.get(Property.GENERAL_OPENTELEMETRY_FACTORY),
+        conf.get(Property.GENERAL_OPENTELEMETRY_NAME));
   }
 
-  private static void enableTracing(String hostname, String service, String 
spanReceivers,
-      String zookeepers, long timeout, String zkPath, Map<String,String> 
spanReceiverProps) {
-
-    Map<String,
-        String> htraceConfigProps = 
spanReceiverProps.entrySet().stream().collect(Collectors.toMap(
-            k -> 
String.valueOf(k).substring(Property.TRACE_SPAN_RECEIVER_PREFIX.getKey().length()),
-            String::valueOf, (a, b) -> {
-              throw new AssertionError("duplicate can't happen");
-            }, HashMap::new));
-    htraceConfigProps.put(TRACER_ZK_HOST, zookeepers);
-    htraceConfigProps.put(TRACER_ZK_TIMEOUT, Long.toString(timeout));
-    htraceConfigProps.put(TRACER_ZK_PATH, zkPath);
-    if (hostname != null) {
-      htraceConfigProps.put(TRACE_HOST_PROPERTY, hostname);
-    }
-    if (service != null) {
-      htraceConfigProps.put(TRACE_SERVICE_PROPERTY, service);
-    }
-    ShutdownHookManager.get().addShutdownHook(TraceUtil::disable, 0);
-    synchronized (receivers) {
-      if (!receivers.isEmpty()) {
-        log.info("Already loaded span receivers, enable tracing does not need 
to be called again");
-        return;
-      }
-      if (spanReceivers == null) {
-        return;
-      }
-      Stream.of(spanReceivers.split(",")).map(String::trim).filter(s -> 
!s.isEmpty())
-          .forEach(className -> {
-            HTraceConfiguration htraceConf = 
HTraceConfiguration.fromMap(htraceConfigProps);
-            SpanReceiverBuilder builder = new SpanReceiverBuilder(htraceConf);
-            SpanReceiver rcvr = 
builder.spanReceiverClass(className.trim()).build();
-            if (rcvr == null) {
-              log.warn("Failed to load SpanReceiver {}", className);
-            } else {
-              receivers.add(rcvr);
-              log.debug("SpanReceiver {} was loaded successfully.", className);
-            }
-          });
-      for (SpanReceiver rcvr : receivers) {
-        Trace.addReceiver(rcvr);
+  /**
+   * If not enabled, the OpenTelemetry implementation will be set to the NoOp 
implementation. If
+   * enabled and a factoryClass is supplied, then we will get the 
OpenTelemetry instance from the
+   * factory class.
+   *
+   * @param enabled
+   *          whether or not tracing is enabled
+   * @param factoryClass
+   *          name of class to load
+   * @param instrumentationName
+   *          OpenTelemetry instrumentation library name
+   * @throws Exception
+   *           unable to find or load class
+   */
+  public static void initializeTracer(boolean enabled, String factoryClass,
+      String instrumentationName) throws Exception {
+    if (instance == null) {
+      OpenTelemetry ot = null;
+      if (!enabled) {
+        ot = OpenTelemetry.noop();
+      } else if (factoryClass != null && !factoryClass.isEmpty()) {
+        Class<? extends OpenTelemetryFactory> clazz =
+            (Class<? extends OpenTelemetryFactory>) 
ClassLoaderUtil.loadClass(factoryClass,
+                OpenTelemetryFactory.class);
+        OpenTelemetryFactory factory = 
clazz.getDeclaredConstructor().newInstance();
+        ot = factory.getOpenTelemetry();
+        LOG.info("OpenTelemetry configured and set from {}", clazz);
+      } else {
+        ot = GlobalOpenTelemetry.get();
       }
+      initializeInternals(ot, instrumentationName);
+    } else {
+      LOG.warn("Tracer already initialized.");
     }
   }
 
   /**
-   * Disable tracing by closing SpanReceivers for the current process.
+   * @return the Tracer set on the GlobalOpenTelemetry object
    */
-  public static void disable() {
-    synchronized (receivers) {
-      receivers.forEach(rcvr -> {
-        try {
-          rcvr.close();
-        } catch (IOException e) {
-          log.warn("Unable to close SpanReceiver correctly: {}", 
e.getMessage(), e);
-        }
-      });
-      receivers.clear();
+  private static Tracer getTracer() {
+    if (Objects.isNull(instance)) {
+      LOG.warn("initializeTracer not called, using 
GlobalOpenTelemetry.getTracer()");
+      instance = GlobalOpenTelemetry.getTracer(name);
+      tracing = (!instance.equals(OpenTelemetry.noop().getTracer(name)));
+      LOG.info("Trace enabled: {}, Tracer is: {}", tracing, 
instance.getClass());
     }
+    return instance;
   }
 
   /**
-   * Continue a trace by starting a new span with a given parent and 
description.
+   * @return true if an OpenTelemetry Tracer implementation has been set, 
false if the NoOp Tracer
+   *         is being used.
    */
-  public static TraceScope trace(TInfo info, String description) {
-    return info.traceId == 0 ? NullScope.INSTANCE
-        : Trace.startSpan(description, new TraceInfo(info.traceId, 
info.parentId));
+  public static boolean isTracing() {
+    return tracing;
   }
 
-  private static final TInfo DONT_TRACE = new TInfo(0, 0);
+  public static Span createSpan(Class<?> caller, String spanName, SpanKind 
kind) {
+    return createSpan(caller, spanName, kind, null, false, null);
+  }
 
-  /**
-   * Obtain {@link org.apache.accumulo.core.trace.thrift.TInfo} for the 
current span.
-   */
-  public static TInfo traceInfo() {
-    Span span = Trace.currentSpan();
-    if (span != null) {
-      return new TInfo(span.getTraceId(), span.getSpanId());
+  public static Span createSpan(Class<?> caller, String spanName, SpanKind 
kind, Context parent) {
+    return createSpan(caller, spanName, kind, null, false, parent);
+  }
+
+  public static Span createSpan(Class<?> caller, String spanName, SpanKind 
kind,
+      Map<String,String> attributes) {
+    return createSpan(caller, spanName, kind, attributes, false, null);
+  }
+
+  public static Span createSpan(Class<?> caller, String spanName, SpanKind 
kind,
+      Map<String,String> attributes, boolean setNoParent, Context parent) {
+    final String name = String.format(SPAN_FORMAT, caller.getSimpleName(), 
spanName);
+    final SpanBuilder builder = getTracer().spanBuilder(name);
+    builder.setSpanKind(kind);
+    if (attributes != null) {
+      attributes.forEach((k, v) -> builder.setAttribute(k, v));
     }
-    return DONT_TRACE;
+    if (setNoParent) {
+      builder.setNoParent();
+    } else if (parent != null) {
+      builder.setParent(parent);
+    }
+    return builder.startSpan();
   }
 
-  public static CountSampler countSampler(long frequency) {
-    return new CountSampler(HTraceConfiguration.fromMap(Collections
-        .singletonMap(CountSampler.SAMPLER_FREQUENCY_CONF_KEY, 
Long.toString(frequency))));
+  /**
+   * Record that an Exception occurred in the code covered by a Span
+   *
+   * @param span
+   *          the span
+   * @param e
+   *          the exception
+   * @param rethrown
+   *          whether the exception is subsequently re-thrown
+   */
+  public static void setException(Span span, Throwable e, boolean rethrown) {
+    if (tracing) {
+      span.setStatus(StatusCode.ERROR);
+      span.recordException(e,
+          Attributes.builder().put(SemanticAttributes.EXCEPTION_TYPE, 
e.getClass().getName())
+              .put(SemanticAttributes.EXCEPTION_MESSAGE, e.getMessage())
+              .put(SemanticAttributes.EXCEPTION_ESCAPED, rethrown).build());
+    }
   }
 
-  public static ProbabilitySampler probabilitySampler(double fraction) {
-    return new ProbabilitySampler(HTraceConfiguration.fromMap(Collections
-        .singletonMap(ProbabilitySampler.SAMPLER_FRACTION_CONF_KEY, 
Double.toString(fraction))));
+  /**
+   * Obtain {@link org.apache.accumulo.core.trace.thrift.TInfo} for the 
current context. This is
+   * used to send the current trace information to a remote process
+   */
+  public static TInfo traceInfo() {

Review comment:
       This method's name seems to understate the role it is performing. I 
think a better name would be something like `currentTraceContextInfo`, but 
that's a bit long.

##########
File path: 
minicluster/src/main/java/org/apache/accumulo/miniclusterImpl/MiniAccumuloConfigImpl.java
##########
@@ -160,7 +162,9 @@ MiniAccumuloConfigImpl initialize() {
       mergeProp(Property.GC_CYCLE_DELAY.getKey(), "4s");
       mergeProp(Property.GC_CYCLE_START.getKey(), "0s");
       mergePropWithRandomPort(Property.MANAGER_CLIENTPORT.getKey());
-      mergePropWithRandomPort(Property.TRACE_PORT.getKey());
+      @SuppressWarnings("deprecation")
+      final String tracePort = Property.TRACE_PORT.getKey();
+      mergePropWithRandomPort(tracePort);

Review comment:
       Unused property need not be written to the file.
   ```suggestion
   ```

##########
File path: 
server/base/src/main/java/org/apache/accumulo/server/util/ChangeSecret.java
##########
@@ -63,18 +67,21 @@
   public static void main(String[] args) throws Exception {
     var siteConfig = SiteConfiguration.auto();
     var hadoopConf = new Configuration();
+
     Opts opts = new Opts();
-    List<String> argsList = new ArrayList<>(args.length + 2);
-    argsList.add("--old");
-    argsList.add("--new");
-    argsList.addAll(Arrays.asList(args));
-    try (TraceScope clientSpan =
-        opts.parseArgsAndTrace(ChangeSecret.class.getName(), 
argsList.toArray(new String[0]))) {
-
-      ServerContext context = opts.getServerContext();
-      try (var fs = context.getVolumeManager()) {
-        ServerDirs serverDirs = new ServerDirs(siteConfig, hadoopConf);
-        verifyHdfsWritePermission(serverDirs, fs);
+    ServerContext context = opts.getServerContext();
+    try (var fs = context.getVolumeManager()) {
+      ServerDirs serverDirs = new ServerDirs(siteConfig, hadoopConf);
+      verifyHdfsWritePermission(serverDirs, fs);
+
+      List<String> argsList = new ArrayList<>(args.length + 2);
+      argsList.add("--old");
+      argsList.add("--new");
+      argsList.addAll(Arrays.asList(args));
+
+      opts.parseArgs(ChangeSecret.class.getName(), args);
+      Span span = TraceUtil.createSpan(ChangeSecret.class, "main", 
SpanKind.CLIENT);
+      try (Scope scope = span.makeCurrent()) {

Review comment:
       It's not clear why this was moved inside here, instead of wrapping all 
of it, including the `verifyHdfsWritePermission` call above.

##########
File path: pom.xml
##########
@@ -177,6 +175,13 @@
         <type>pom</type>
         <scope>import</scope>
       </dependency>
+      <dependency>
+        <groupId>io.opentelemetry</groupId>
+        <artifactId>opentelemetry-bom</artifactId>
+        <version>1.6.0</version>

Review comment:
       ```suggestion
           <version>1.7.1</version>
   ```

##########
File path: 
server/base/src/main/java/org/apache/accumulo/server/util/CheckForMetadataProblems.java
##########
@@ -175,14 +179,20 @@ private static void 
checkMetadataAndRootTableEntries(String tableNameToCheck, Se
 
   public static void main(String[] args) throws Exception {
     opts = new ServerUtilOpts();
-    try (TraceScope clientSpan =
-        opts.parseArgsAndTrace(CheckForMetadataProblems.class.getName(), 
args)) {
+    opts.parseArgs(CheckForMetadataProblems.class.getName(), args);
+    Span span = TraceUtil.createSpan(CheckForMetadataProblems.class, "main", 
SpanKind.CLIENT);
+    try (Scope scope = span.makeCurrent()) {
 
       checkMetadataAndRootTableEntries(RootTable.NAME, opts);
       System.out.println();
       checkMetadataAndRootTableEntries(MetadataTable.NAME, opts);
       if (sawProblems)
         throw new RuntimeException();
+    } catch (Exception e) {
+      TraceUtil.setException(span, e, true);
+      throw e;

Review comment:
       I really wanted to try to wrap Span and Scope into a single Closeable 
wrapper object, so we don't have to explicitly call span.end(), and it would 
make the diff smaller, but I now realize that wouldn't work. There needs to be 
an object not auto-closed, that is still "open" so you can update it with the 
exception-stuff. It wouldn't work my way, because `close()` is called on 
resources prior to the catch blocks executing. My way would only work if we did 
not want to set exceptions on the Span.
   
   It may still be worth it to create wrapper objects in order to hide 
OpenTelemetry types behind an abstraction, so we don't have OpenTelemetry 
imports scattered throughout, and harder to upgrade or switch to another impl 
if things change in future, but so long as we want to set attributes in a catch 
clause, we'll need both a Span and a Scope type of some kind, even if they both 
wrap their respective OpenTelemetry types.

##########
File path: pom.xml
##########
@@ -301,6 +306,11 @@
         <artifactId>commons-logging</artifactId>
         <version>1.2</version>
       </dependency>
+      <dependency>
+        <groupId>io.opentelemetry</groupId>
+        <artifactId>opentelemetry-semconv</artifactId>
+        <version>1.6.0-alpha</version>

Review comment:
       It's not clear what this dependency provides us, but it turns out 
there's a separate BOM we should be using to manage all the dependency versions 
for the alpha (unstable) modules.
   ```suggestion
           <artifactId>opentelemetry-bom-alpha</artifactId>
           <version>1.7.1-alpha</version>
           <type>pom</type>
           <scope>import</scope>
   ```

##########
File path: core/src/main/java/org/apache/accumulo/core/conf/Property.java
##########
@@ -606,8 +612,10 @@
       "1.8.0"),
   TSERV_MINC_MAXCONCURRENT("tserver.compaction.minor.concurrent.max", "4", 
PropertyType.COUNT,
       "The maximum number of concurrent minor compactions for a tablet 
server", "1.3.5"),
+  @Deprecated(since = "2.1.0", forRemoval = true)
   TSERV_MAJC_TRACE_PERCENT("tserver.compaction.major.trace.percent", "0.1", 
PropertyType.FRACTION,

Review comment:
       I looked into setting samplers, but it looks like we have to use the SDK 
for that to create our own, more fully robust, instrumentation. But that kinda 
precludes using user-pluggable OpenTelemetry instances. Unfortunately, you 
can't just set the sampler on the specific Tracer instance when you get it from 
the OpenTelemetry instance. This seems like a downgrade from what HTrace made 
available.




-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


Reply via email to