InstrumentationModule.java 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. /*
  2. * Copyright The OpenTelemetry Authors
  3. * SPDX-License-Identifier: Apache-2.0
  4. */
  5. package io.opentelemetry.javaagent.extension.instrumentation;
  6. import static java.util.Arrays.asList;
  7. import static java.util.Collections.unmodifiableSet;
  8. import static net.bytebuddy.matcher.ElementMatchers.any;
  9. import io.opentelemetry.instrumentation.api.config.Config;
  10. import io.opentelemetry.javaagent.extension.Ordered;
  11. import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
  12. import java.util.Collections;
  13. import java.util.LinkedHashSet;
  14. import java.util.List;
  15. import java.util.Set;
  16. import net.bytebuddy.matcher.ElementMatcher;
  17. /**
  18. * Instrumentation module groups several connected {@link TypeInstrumentation}s together, sharing
  19. * class loader matcher, helper classes, muzzle safety checks, etc. Ideally all types in a single
  20. * instrumented library should live in a single module.
  21. *
  22. * <p>Classes extending {@link InstrumentationModule} should be public and non-final so that it's
  23. * possible to extend and reuse them in vendor distributions.
  24. *
  25. * <p>{@link InstrumentationModule} is an SPI, you need to ensure that a proper {@code
  26. * META-INF/services/} provider file is created for it to be picked up by the agent. See {@link
  27. * java.util.ServiceLoader} for more details.
  28. */
  29. public abstract class InstrumentationModule implements Ordered {
  30. private static final boolean DEFAULT_ENABLED =
  31. Config.get().getBoolean("otel.instrumentation.common.default-enabled", true);
  32. private final Set<String> instrumentationNames;
  33. /**
  34. * Creates an instrumentation module. Note that all implementations of {@link
  35. * InstrumentationModule} must have a default constructor (for SPI), so they have to pass the
  36. * instrumentation names to the super class constructor.
  37. *
  38. * <p>The instrumentation names should follow several rules:
  39. *
  40. * <ul>
  41. * <li>Instrumentation names should consist of hyphen-separated words, e.g. {@code
  42. * instrumented-library};
  43. * <li>In general, instrumentation names should be the as close as possible to the gradle module
  44. * name - which in turn should be as close as possible to the instrumented library name;
  45. * <li>The main instrumentation name should be the same as the gradle module name, minus the
  46. * version if it's a part of the module name. When several versions of a library are
  47. * instrumented they should all share the same main instrumentation name so that it's easy
  48. * to enable/disable the instrumentation regardless of the runtime library version;
  49. * <li>If the gradle module has a version as a part of its name, an additional instrumentation
  50. * name containing the version should be passed, e.g. {@code instrumented-library-1.0}.
  51. * </ul>
  52. */
  53. protected InstrumentationModule(
  54. String mainInstrumentationName, String... additionalInstrumentationNames) {
  55. LinkedHashSet<String> names = new LinkedHashSet<>(additionalInstrumentationNames.length + 1);
  56. names.add(mainInstrumentationName);
  57. names.addAll(asList(additionalInstrumentationNames));
  58. this.instrumentationNames = unmodifiableSet(names);
  59. }
  60. /**
  61. * Returns all instrumentation names assigned to this module. See {@link
  62. * #InstrumentationModule(String, String...)} for more details about instrumentation names.
  63. */
  64. public final Set<String> instrumentationNames() {
  65. return instrumentationNames;
  66. }
  67. /**
  68. * Returns the main instrumentation name. See {@link #InstrumentationModule(String, String...)}
  69. * for more details about instrumentation names.
  70. */
  71. public final String instrumentationName() {
  72. return instrumentationNames.iterator().next();
  73. }
  74. /**
  75. * Allows instrumentation modules to disable themselves by default, or to additionally disable
  76. * themselves on some other condition.
  77. *
  78. * @deprecated Use {@link #defaultEnabled(ConfigProperties)} instead.
  79. */
  80. @Deprecated
  81. public boolean defaultEnabled() {
  82. return DEFAULT_ENABLED;
  83. }
  84. /**
  85. * Allows instrumentation modules to disable themselves by default, or to additionally disable
  86. * themselves on some other condition.
  87. */
  88. public boolean defaultEnabled(ConfigProperties config) {
  89. return defaultEnabled();
  90. }
  91. /**
  92. * Instrumentation modules can override this method to specify additional packages (or classes)
  93. * that should be treated as "library instrumentation" packages. Classes from those packages will
  94. * be treated by muzzle as instrumentation helper classes: they will be scanned for references and
  95. * automatically injected into the application class loader if they're used in any type
  96. * instrumentation. The classes for which this predicate returns {@code true} will be treated as
  97. * helper classes, in addition to the default ones defined in the {@code HelperClassPredicate}
  98. * class.
  99. *
  100. * @param className The name of the class that may or may not be a helper class.
  101. */
  102. public boolean isHelperClass(String className) {
  103. return false;
  104. }
  105. /** Register resource names to inject into the user's class loader. */
  106. public void registerHelperResources(HelperResourceBuilder helperResourceBuilder) {}
  107. /**
  108. * An instrumentation module can implement this method to make sure that the class loader contains
  109. * the particular library version. It is useful to implement that if the muzzle check does not
  110. * fail for versions out of the instrumentation's scope.
  111. *
  112. * <p>E.g. supposing version 1.0 has class {@code A}, but it was removed in version 2.0; A is not
  113. * used in the helper classes at all; this module is instrumenting 2.0: this method will return
  114. * {@code not(hasClassesNamed("A"))}.
  115. *
  116. * @return A type matcher used to match the class loader under transform
  117. */
  118. public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() {
  119. return any();
  120. }
  121. /** Returns a list of all individual type instrumentation in this module. */
  122. public abstract List<TypeInstrumentation> typeInstrumentations();
  123. /**
  124. * Returns a list of additional instrumentation helper classes, which are not automatically
  125. * detected during compile time.
  126. *
  127. * <p>If your instrumentation module does not apply and you see warnings about missing classes in
  128. * the logs, you may need to override this method and provide fully qualified classes names of
  129. * helper classes that your instrumentation uses.
  130. *
  131. * <p>These helper classes will be injected into the application class loader after automatically
  132. * detected ones.
  133. */
  134. public List<String> getAdditionalHelperClassNames() {
  135. return Collections.emptyList();
  136. }
  137. }