You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by st...@apache.org on 2023/09/14 23:18:24 UTC

[solr] branch branch_9x updated: SOLR-16938 Auto configure tracer without a tag in solr.xml

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

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


The following commit(s) were added to refs/heads/branch_9x by this push:
     new ff385d7b63c SOLR-16938 Auto configure tracer without a <tracerConfig> tag in solr.xml
ff385d7b63c is described below

commit ff385d7b63c913b9724876b359cae5c63455f13e
Author: Alex Deparvu <st...@apache.org>
AuthorDate: Thu Sep 14 16:17:03 2023 -0700

    SOLR-16938 Auto configure tracer without a <tracerConfig> tag in solr.xml
    
    (manual cherry picked from commit 5f25f09de4652c1c48095e45f68c2f0e28f8a464)
---
 solr/CHANGES.txt                                   |  2 +
 .../org/apache/solr/core/TracerConfigurator.java   | 64 ++++++++++++++++++++++
 .../apache/solr/core/TestTracerConfigurator.java   | 63 +++++++++++++++++++++
 solr/modules/opentelemetry/build.gradle            |  1 +
 .../solr/opentelemetry/OtelTracerConfigurator.java | 37 ++++++++-----
 .../opentelemetry/OtelTracerConfiguratorTest.java  | 10 +++-
 .../pages/distributed-tracing.adoc                 |  2 +
 7 files changed, 162 insertions(+), 17 deletions(-)

diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 21055518960..3e200832508 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -68,6 +68,8 @@ Improvements
 * SOLR-16461: `/solr/coreName/replication?command=backup` now has a v2 equivalent, available at
   `/api/cores/coreName/replication/backups` (Sanjay Dutt, Jason Gerlowski, Alex Deparvu)
 
+* SOLR-16938: Auto configure tracer without a <tracerConfig> tag in solr.xml (Alex Deparvu)
+
 Optimizations
 ---------------------
 
diff --git a/solr/core/src/java/org/apache/solr/core/TracerConfigurator.java b/solr/core/src/java/org/apache/solr/core/TracerConfigurator.java
index c7259e0b7be..30ce2071465 100644
--- a/solr/core/src/java/org/apache/solr/core/TracerConfigurator.java
+++ b/solr/core/src/java/org/apache/solr/core/TracerConfigurator.java
@@ -22,8 +22,12 @@ import io.opentracing.Span;
 import io.opentracing.Tracer;
 import io.opentracing.util.GlobalTracer;
 import java.lang.invoke.MethodHandles;
+import java.util.Locale;
+import java.util.Map;
 import java.util.concurrent.atomic.AtomicReference;
+import org.apache.solr.common.SolrException;
 import org.apache.solr.common.util.ExecutorUtil;
+import org.apache.solr.common.util.NamedList;
 import org.apache.solr.util.plugin.NamedListInitializedPlugin;
 import org.apache.solr.util.tracing.SimplePropagator;
 import org.slf4j.Logger;
@@ -36,6 +40,10 @@ public abstract class TracerConfigurator implements NamedListInitializedPlugin {
   public static final boolean TRACE_ID_GEN_ENABLED =
       Boolean.parseBoolean(System.getProperty("solr.alwaysOnTraceId", "true"));
 
+  private static final String DEFAULT_CLASS_NAME =
+      System.getProperty(
+          "solr.otelDefaultConfigurator", "org.apache.solr.opentelemetry.OtelTracerConfigurator");
+
   public abstract Tracer getTracer();
 
   public static Tracer loadTracer(SolrResourceLoader loader, PluginInfo info) {
@@ -52,6 +60,8 @@ public abstract class TracerConfigurator implements NamedListInitializedPlugin {
             configurator.init(info.initArgs);
             return configurator.getTracer();
           });
+    } else if (shouldAutoConfigOTEL()) {
+      GlobalTracer.registerIfAbsent(() -> autoConfigOTEL(loader));
     } else if (TRACE_ID_GEN_ENABLED) {
       SimplePropagator.load();
     }
@@ -102,4 +112,58 @@ public abstract class TracerConfigurator implements NamedListInitializedPlugin {
       }
     }
   }
+
+  private static Tracer autoConfigOTEL(SolrResourceLoader loader) {
+    try {
+      TracerConfigurator configurator =
+          loader.newInstance(DEFAULT_CLASS_NAME, TracerConfigurator.class);
+      configurator.init(new NamedList<>());
+      return configurator.getTracer();
+    } catch (SolrException e) {
+      log.error(
+          "Unable to auto-config OpenTelemetry with class {}. Make sure you have enabled the 'opentelemetry' module",
+          DEFAULT_CLASS_NAME,
+          e);
+    }
+    return GlobalTracer.get();
+  }
+
+  /**
+   * Best effort way to determine if we should attempt to init OTEL from system properties.
+   *
+   * @return true if OTEL should be init
+   */
+  static boolean shouldAutoConfigOTEL() {
+    var env = System.getenv();
+    boolean isSdkDisabled = Boolean.parseBoolean(getConfig("OTEL_SDK_DISABLED", env));
+    if (isSdkDisabled) {
+      return false;
+    }
+    return getConfig("OTEL_SERVICE_NAME", env) != null;
+  }
+
+  /**
+   * Returns system property if found, else returns environment variable, or null if none found.
+   *
+   * @param envName the environment variable to look for
+   * @param env current env
+   * @return the resolved value
+   */
+  protected static String getConfig(String envName, Map<String, String> env) {
+    String sysName = envNameToSyspropName(envName);
+    String sysValue = System.getProperty(sysName);
+    String envValue = env.get(envName);
+    return sysValue != null ? sysValue : envValue;
+  }
+
+  /**
+   * In OTEL Java SDK there is a convention that the java property name for OTEL_FOO_BAR is
+   * otel.foo.bar
+   *
+   * @param envName the environmnet name to convert
+   * @return the corresponding sysprop name
+   */
+  protected static String envNameToSyspropName(String envName) {
+    return envName.toLowerCase(Locale.ROOT).replace("_", ".");
+  }
 }
diff --git a/solr/core/src/test/org/apache/solr/core/TestTracerConfigurator.java b/solr/core/src/test/org/apache/solr/core/TestTracerConfigurator.java
new file mode 100644
index 00000000000..a0bec839e91
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/core/TestTracerConfigurator.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.core;
+
+import io.opentracing.util.GlobalTracer;
+import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.common.util.ExecutorUtil;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class TestTracerConfigurator extends SolrTestCaseJ4 {
+
+  @BeforeClass
+  public static void setUpProperties() throws Exception {
+    System.setProperty("otel.service.name", "something");
+    System.setProperty("solr.otelDefaultConfigurator", "configuratorClassDoesNotExistTest");
+  }
+
+  @AfterClass
+  public static void clearProperties() throws Exception {
+    System.clearProperty("solr.otelDefaultConfigurator");
+    System.clearProperty("otel.service.name");
+  }
+
+  @Test
+  public void configuratorClassDoesNotExistTest() {
+    // to be safe because this test tests tracing.
+    resetGlobalTracer();
+    ExecutorUtil.resetThreadLocalProviders();
+    assertTrue(TracerConfigurator.shouldAutoConfigOTEL());
+    SolrResourceLoader loader = new SolrResourceLoader(TEST_PATH().resolve("collection1"));
+    TracerConfigurator.loadTracer(loader, null);
+
+    assertTrue(
+        "Expecting noop otel after failure to auto-init",
+        GlobalTracer.get().toString().contains("GlobalTracer"));
+  }
+
+  @Test
+  public void otelDisabledByProperty() {
+    System.setProperty("otel.sdk.disabled", "true");
+    try {
+      assertFalse(TracerConfigurator.shouldAutoConfigOTEL());
+    } finally {
+      System.clearProperty("otel.sdk.disabled");
+    }
+  }
+}
diff --git a/solr/modules/opentelemetry/build.gradle b/solr/modules/opentelemetry/build.gradle
index 225068b48e9..78119966758 100644
--- a/solr/modules/opentelemetry/build.gradle
+++ b/solr/modules/opentelemetry/build.gradle
@@ -21,6 +21,7 @@ description = 'Open Telemetry (OTEL) tracer'
 
 dependencies {
   implementation project(':solr:core')
+  implementation project(':solr:solrj')
 
   implementation platform('io.opentelemetry:opentelemetry-bom')
 
diff --git a/solr/modules/opentelemetry/src/java/org/apache/solr/opentelemetry/OtelTracerConfigurator.java b/solr/modules/opentelemetry/src/java/org/apache/solr/opentelemetry/OtelTracerConfigurator.java
index 6b876c7c1b6..d892ee20f7e 100644
--- a/solr/modules/opentelemetry/src/java/org/apache/solr/opentelemetry/OtelTracerConfigurator.java
+++ b/solr/modules/opentelemetry/src/java/org/apache/solr/opentelemetry/OtelTracerConfigurator.java
@@ -27,6 +27,7 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.Objects;
 import java.util.stream.Collectors;
+import org.apache.solr.common.util.NamedList;
 import org.apache.solr.core.TracerConfigurator;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -41,6 +42,11 @@ public class OtelTracerConfigurator extends TracerConfigurator {
   // Copy of environment. Can be overridden by tests
   Map<String, String> currentEnv = System.getenv();
 
+  @Override
+  public void init(NamedList<?> args) {
+    injectPluginSettingsIfNotConfigured(args);
+  }
+
   @Override
   public Tracer getTracer() {
     setDefaultIfNotConfigured("OTEL_SERVICE_NAME", "solr");
@@ -68,6 +74,20 @@ public class OtelTracerConfigurator extends TracerConfigurator {
     return new ClosableTracerShim(shim, otelSdk.getSdkTracerProvider());
   }
 
+  /**
+   * Will inject plugin configuration values into system properties if not already setup (existing
+   * system properties take precedence)
+   */
+  private void injectPluginSettingsIfNotConfigured(NamedList<?> args) {
+    args.forEach(
+        (k, v) -> {
+          var asSysName = envNameToSyspropName(k);
+          if (asSysName.startsWith("otel.")) {
+            setDefaultIfNotConfigured(asSysName, v.toString());
+          }
+        });
+  }
+
   /**
    * Add explicit tags statically to all traces, independent of request. Attributes with same name
    * supplied in ENV or SysProp will take precedence over attributes added in code.
@@ -124,24 +144,11 @@ public class OtelTracerConfigurator extends TracerConfigurator {
   /**
    * Returns system property if found, else returns environment variable, or null if none found.
    *
-   * @param envName the environment to look for
+   * @param envName the environment variable to look for
    * @return the resolved value
    */
   String getEnvOrSysprop(String envName) {
-    String envValue = currentEnv.get(envName);
-    String propValue = System.getProperty(envNameToSyspropName(envName));
-    return propValue != null ? propValue : envValue;
-  }
-
-  /**
-   * In OTEL Java SDK there is a convention that the java property name for OTEL_FOO_BAR is
-   * otel.foo.bar
-   *
-   * @param envName the environmnet name to convert
-   * @return the corresponding sysprop name
-   */
-  static String envNameToSyspropName(String envName) {
-    return envName.toLowerCase(Locale.ROOT).replace("_", ".");
+    return getConfig(envName, currentEnv);
   }
 
   /**
diff --git a/solr/modules/opentelemetry/src/test/org/apache/solr/opentelemetry/OtelTracerConfiguratorTest.java b/solr/modules/opentelemetry/src/test/org/apache/solr/opentelemetry/OtelTracerConfiguratorTest.java
index c7cda9fbb7a..df2692f1066 100644
--- a/solr/modules/opentelemetry/src/test/org/apache/solr/opentelemetry/OtelTracerConfiguratorTest.java
+++ b/solr/modules/opentelemetry/src/test/org/apache/solr/opentelemetry/OtelTracerConfiguratorTest.java
@@ -23,6 +23,7 @@ import java.util.Map;
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.cloud.MiniSolrCloudCluster;
 import org.apache.solr.common.util.ExecutorUtil;
+import org.apache.solr.common.util.NamedList;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -44,6 +45,10 @@ public class OtelTracerConfiguratorTest extends SolrTestCaseJ4 {
     System.setProperty("otelnothere", "bar");
     System.setProperty("otel.k1", "prop-k1");
     System.setProperty("otel.k3", "prop-k3");
+    NamedList<String> conf = new NamedList<>();
+    conf.add("OTEL_K1", "conf-k1");
+    conf.add("otel.k7", "conf-k7");
+    instance.init(conf);
 
     // to be safe because this test tests tracing.
     resetGlobalTracer();
@@ -66,14 +71,15 @@ public class OtelTracerConfiguratorTest extends SolrTestCaseJ4 {
         Map.of(
             "OTEL_K1", "prop-k1",
             "OTEL_K2", "env-k2",
-            "OTEL_K3", "prop-k3");
+            "OTEL_K3", "prop-k3",
+            "OTEL_K7", "conf-k7");
     assertEquals(expected, instance.getCurrentOtelConfig());
   }
 
   @Test
   public void testGetCurrentOtelConfigAsString() {
     assertEquals(
-        "OTEL_K1=prop-k1; OTEL_K2=env-k2; OTEL_K3=prop-k3",
+        "OTEL_K1=prop-k1; OTEL_K2=env-k2; OTEL_K3=prop-k3; OTEL_K7=conf-k7",
         instance.getCurrentOtelConfigAsString());
   }
 
diff --git a/solr/solr-ref-guide/modules/deployment-guide/pages/distributed-tracing.adoc b/solr/solr-ref-guide/modules/deployment-guide/pages/distributed-tracing.adoc
index fcd336c84f4..aea35725464 100644
--- a/solr/solr-ref-guide/modules/deployment-guide/pages/distributed-tracing.adoc
+++ b/solr/solr-ref-guide/modules/deployment-guide/pages/distributed-tracing.adoc
@@ -64,6 +64,8 @@ This module brings support for the industry standard https://opentelemetry.io[Op
 <tracerConfig name="tracerConfig" class="org.apache.solr.opentelemetry.OtelTracerConfigurator"/>
 ----
 
+As an alternative to changing the `solr.xml` file, the `OTEL` tracer will be enabled if the system property `otel.service.name` or environment variable `OTEL_SERVICE_NAME` is present. The `opentelemetry` module still needs to be enabled for the tracer to work.
+
 Enable the module with either system property `-Dsolr.modules=opentelemetry` or environment variable `SOLR_MODULES=opentelemetry`.
 
 === Configuration