|  | 
| 10 | 10 | import io.opentelemetry.sdk.autoconfigure.spi.AutoConfigurationCustomizerProvider; | 
| 11 | 11 | import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; | 
| 12 | 12 | import io.opentelemetry.sdk.trace.ReadableSpan; | 
|  | 13 | + | 
|  | 14 | +import javax.annotation.Nullable; | 
| 13 | 15 | import java.lang.reflect.Constructor; | 
| 14 | 16 | import java.lang.reflect.InvocationTargetException; | 
| 15 | 17 | import java.time.Duration; | 
| 16 | 18 | import java.util.function.Predicate; | 
| 17 | 19 | import java.util.logging.Level; | 
| 18 | 20 | import java.util.logging.Logger; | 
| 19 |  | -import javax.annotation.Nullable; | 
| 20 | 21 | 
 | 
| 21 | 22 | @AutoService(AutoConfigurationCustomizerProvider.class) | 
| 22 | 23 | public class StackTraceAutoConfig implements AutoConfigurationCustomizerProvider { | 
| 23 | 24 | 
 | 
| 24 |  | -  private static final Logger log = Logger.getLogger(StackTraceAutoConfig.class.getName()); | 
|  | 25 | +    private static final Logger log = Logger.getLogger( | 
|  | 26 | +            StackTraceAutoConfig.class.getName()); | 
| 25 | 27 | 
 | 
| 26 |  | -  private static final String CONFIG_MIN_DURATION = | 
| 27 |  | -      "otel.java.experimental.span-stacktrace.min.duration"; | 
| 28 |  | -  private static final Duration CONFIG_MIN_DURATION_DEFAULT = Duration.ofMillis(5); | 
|  | 28 | +    private static final String CONFIG_MIN_DURATION = | 
|  | 29 | +            "otel.java.experimental.span-stacktrace.min.duration"; | 
|  | 30 | +    private static final Duration CONFIG_MIN_DURATION_DEFAULT = Duration.ofMillis(5); | 
| 29 | 31 | 
 | 
| 30 |  | -  private static final String CONFIG_FILTER = "otel.java.experimental.span-stacktrace.filter"; | 
|  | 32 | +    private static final String CONFIG_FILTER = "otel.java.experimental.span-stacktrace.filter"; | 
| 31 | 33 | 
 | 
| 32 |  | -  @Override | 
| 33 |  | -  public void customize(AutoConfigurationCustomizer config) { | 
| 34 |  | -    config.addTracerProviderCustomizer( | 
| 35 |  | -        (providerBuilder, properties) -> { | 
| 36 |  | -          long minDuration = getMinDuration(properties); | 
| 37 |  | -          if (minDuration >= 0) { | 
| 38 |  | -            Predicate<ReadableSpan> filter = getFilterPredicate(properties); | 
| 39 |  | -            providerBuilder.addSpanProcessor(new StackTraceSpanProcessor(minDuration, filter)); | 
| 40 |  | -          } | 
| 41 |  | -          return providerBuilder; | 
| 42 |  | -        }); | 
| 43 |  | -  } | 
| 44 |  | - | 
| 45 |  | -  // package-private for testing | 
| 46 |  | -  static long getMinDuration(ConfigProperties properties) { | 
| 47 |  | -    long minDuration = | 
| 48 |  | -        properties.getDuration(CONFIG_MIN_DURATION, CONFIG_MIN_DURATION_DEFAULT).toNanos(); | 
| 49 |  | -    if (minDuration < 0) { | 
| 50 |  | -      log.fine("Stack traces capture is disabled"); | 
| 51 |  | -    } else { | 
| 52 |  | -      log.log( | 
| 53 |  | -          Level.FINE, | 
| 54 |  | -          "Stack traces will be added to spans with a minimum duration of {0} nanos", | 
| 55 |  | -          minDuration); | 
|  | 34 | +    @Override | 
|  | 35 | +    public void customize(AutoConfigurationCustomizer config) { | 
|  | 36 | +        config.addTracerProviderCustomizer( | 
|  | 37 | +                (providerBuilder, properties) -> { | 
|  | 38 | +                    long minDuration = getMinDuration(properties); | 
|  | 39 | +                    if (minDuration >= 0) { | 
|  | 40 | +                        Predicate<ReadableSpan> filter = getFilterPredicate(properties); | 
|  | 41 | +                        providerBuilder.addSpanProcessor(new StackTraceSpanProcessor(minDuration, filter)); | 
|  | 42 | +                    } | 
|  | 43 | +                    return providerBuilder; | 
|  | 44 | +                }); | 
| 56 | 45 |     } | 
| 57 |  | -    return minDuration; | 
| 58 |  | -  } | 
| 59 | 46 | 
 | 
| 60 |  | -  // package private for testing | 
| 61 |  | -  static Predicate<ReadableSpan> getFilterPredicate(ConfigProperties properties) { | 
| 62 |  | -    String filterClass = properties.getString(CONFIG_FILTER); | 
| 63 |  | -    Predicate<ReadableSpan> filter = null; | 
| 64 |  | -    if (filterClass != null) { | 
| 65 |  | -      Class<?> filterType = getFilterType(filterClass); | 
| 66 |  | -      if (filterType != null) { | 
| 67 |  | -        filter = getFilterInstance(filterType); | 
| 68 |  | -      } | 
|  | 47 | +    // package-private for testing | 
|  | 48 | +    static long getMinDuration(ConfigProperties properties) { | 
|  | 49 | +        long minDuration = | 
|  | 50 | +                properties.getDuration(CONFIG_MIN_DURATION, CONFIG_MIN_DURATION_DEFAULT).toNanos(); | 
|  | 51 | +        if (minDuration < 0) { | 
|  | 52 | +            log.fine("Stack traces capture is disabled"); | 
|  | 53 | +        } else { | 
|  | 54 | +            log.log( | 
|  | 55 | +                    Level.FINE, | 
|  | 56 | +                    "Stack traces will be added to spans with a minimum duration of {0} nanos", | 
|  | 57 | +                    minDuration); | 
|  | 58 | +        } | 
|  | 59 | +        return minDuration; | 
| 69 | 60 |     } | 
| 70 | 61 | 
 | 
| 71 |  | -    if (filter == null) { | 
| 72 |  | -      // if value is set, lack of filtering is likely an error and must be reported | 
| 73 |  | -      Level disabledLogLevel = filterClass != null ? Level.SEVERE : Level.FINE; | 
| 74 |  | -      log.log(disabledLogLevel, "Span stacktrace filtering disabled"); | 
| 75 |  | -      return span -> true; | 
| 76 |  | -    } else { | 
| 77 |  | -      log.fine("Span stacktrace filtering enabled with: " + filterClass); | 
| 78 |  | -      return filter; | 
|  | 62 | +    // package private for testing | 
|  | 63 | +    static Predicate<ReadableSpan> getFilterPredicate(ConfigProperties properties) { | 
|  | 64 | +        String filterClass = properties.getString(CONFIG_FILTER); | 
|  | 65 | +        Predicate<ReadableSpan> filter = null; | 
|  | 66 | +        if (filterClass != null) { | 
|  | 67 | +            Class<?> filterType = getFilterType(filterClass); | 
|  | 68 | +            if (filterType != null) { | 
|  | 69 | +                filter = getFilterInstance(filterType); | 
|  | 70 | +            } | 
|  | 71 | +        } | 
|  | 72 | + | 
|  | 73 | +        if (filter == null) { | 
|  | 74 | +            // if value is set, lack of filtering is likely an error and must be reported | 
|  | 75 | +            Level disabledLogLevel = filterClass != null ? Level.SEVERE : Level.FINE; | 
|  | 76 | +            log.log(disabledLogLevel, "Span stacktrace filtering disabled"); | 
|  | 77 | +            return span -> true; | 
|  | 78 | +        } else { | 
|  | 79 | +            log.fine("Span stacktrace filtering enabled with: " + filterClass); | 
|  | 80 | +            return filter; | 
|  | 81 | +        } | 
| 79 | 82 |     } | 
| 80 |  | -  } | 
| 81 | 83 | 
 | 
| 82 |  | -  @Nullable | 
| 83 |  | -  private static Class<?> getFilterType(String filterClass) { | 
| 84 |  | -    try { | 
| 85 |  | -      Class<?> filterType = Class.forName(filterClass); | 
| 86 |  | -      if (!Predicate.class.isAssignableFrom(filterType)) { | 
| 87 |  | -        log.severe("Filter must be a subclass of java.util.function.Predicate"); | 
| 88 |  | -        return null; | 
| 89 |  | -      } | 
| 90 |  | -      return filterType; | 
| 91 |  | -    } catch (ClassNotFoundException e) { | 
| 92 |  | -      log.severe("Unable to load filter class: " + filterClass); | 
| 93 |  | -      return null; | 
|  | 84 | +    @Nullable | 
|  | 85 | +    private static Class<?> getFilterType(String filterClass) { | 
|  | 86 | +        try { | 
|  | 87 | +            Class<?> filterType = Class.forName(filterClass); | 
|  | 88 | +            if (!Predicate.class.isAssignableFrom(filterType)) { | 
|  | 89 | +                log.severe("Filter must be a subclass of java.util.function.Predicate"); | 
|  | 90 | +                return null; | 
|  | 91 | +            } | 
|  | 92 | +            return filterType; | 
|  | 93 | +        } catch (ClassNotFoundException e) { | 
|  | 94 | +            log.severe("Unable to load filter class: " + filterClass); | 
|  | 95 | +            return null; | 
|  | 96 | +        } | 
| 94 | 97 |     } | 
| 95 |  | -  } | 
| 96 | 98 | 
 | 
| 97 |  | -  @Nullable | 
| 98 |  | -  @SuppressWarnings("unchecked") | 
| 99 |  | -  private static Predicate<ReadableSpan> getFilterInstance(Class<?> filterType) { | 
| 100 |  | -    try { | 
| 101 |  | -      Constructor<?> constructor = filterType.getConstructor(); | 
| 102 |  | -      return (Predicate<ReadableSpan>) constructor.newInstance(); | 
| 103 |  | -    } catch (NoSuchMethodException | 
| 104 |  | -        | InstantiationException | 
| 105 |  | -        | IllegalAccessException | 
| 106 |  | -        | InvocationTargetException e) { | 
| 107 |  | -      log.severe("Unable to create filter instance with no-arg constructor: " + filterType); | 
| 108 |  | -      return null; | 
|  | 99 | +    @Nullable | 
|  | 100 | +    @SuppressWarnings("unchecked") | 
|  | 101 | +    private static Predicate<ReadableSpan> getFilterInstance(Class<?> filterType) { | 
|  | 102 | +        try { | 
|  | 103 | +            Constructor<?> constructor = filterType.getConstructor(); | 
|  | 104 | +            return (Predicate<ReadableSpan>) constructor.newInstance(); | 
|  | 105 | +        } catch (NoSuchMethodException | 
|  | 106 | +                 | InstantiationException | 
|  | 107 | +                 | IllegalAccessException | 
|  | 108 | +                 | InvocationTargetException e) { | 
|  | 109 | +            log.severe("Unable to create filter instance with no-arg constructor: " + filterType); | 
|  | 110 | +            return null; | 
|  | 111 | +        } | 
| 109 | 112 |     } | 
| 110 |  | -  } | 
| 111 | 113 | } | 
0 commit comments