|  | 
| 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; | 
| 15 | 13 | import java.lang.reflect.Constructor; | 
| 16 | 14 | import java.lang.reflect.InvocationTargetException; | 
| 17 | 15 | import java.time.Duration; | 
| 18 | 16 | import java.util.function.Predicate; | 
| 19 | 17 | import java.util.logging.Level; | 
| 20 | 18 | import java.util.logging.Logger; | 
|  | 19 | +import javax.annotation.Nullable; | 
| 21 | 20 | 
 | 
| 22 | 21 | @AutoService(AutoConfigurationCustomizerProvider.class) | 
| 23 | 22 | public class StackTraceAutoConfig implements AutoConfigurationCustomizerProvider { | 
| 24 | 23 | 
 | 
| 25 |  | -    private static final Logger log = Logger.getLogger( | 
| 26 |  | -            StackTraceAutoConfig.class.getName()); | 
|  | 24 | +  private static final Logger log = Logger.getLogger(StackTraceAutoConfig.class.getName()); | 
| 27 | 25 | 
 | 
| 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); | 
|  | 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); | 
| 31 | 29 | 
 | 
| 32 |  | -    private static final String CONFIG_FILTER = "otel.java.experimental.span-stacktrace.filter"; | 
|  | 30 | +  private static final String CONFIG_FILTER = "otel.java.experimental.span-stacktrace.filter"; | 
| 33 | 31 | 
 | 
| 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 |  | -                }); | 
| 45 |  | -    } | 
|  | 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 | +  } | 
| 46 | 44 | 
 | 
| 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; | 
|  | 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); | 
| 60 | 56 |     } | 
|  | 57 | +    return minDuration; | 
|  | 58 | +  } | 
| 61 | 59 | 
 | 
| 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 |  | -        } | 
|  | 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 | +      } | 
|  | 69 | +    } | 
| 72 | 70 | 
 | 
| 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 |  | -        } | 
|  | 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; | 
| 82 | 79 |     } | 
|  | 80 | +  } | 
| 83 | 81 | 
 | 
| 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 |  | -        } | 
|  | 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; | 
| 97 | 94 |     } | 
|  | 95 | +  } | 
| 98 | 96 | 
 | 
| 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 |  | -        } | 
|  | 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; | 
| 112 | 109 |     } | 
|  | 110 | +  } | 
| 113 | 111 | } | 
0 commit comments