All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule Maven / Gradle / Ivy

There is a newer version: 2.11.0-alpha
Show newest version
/*
 * Copyright The OpenTelemetry Authors
 * SPDX-License-Identifier: Apache-2.0
 */

package io.opentelemetry.javaagent.extension.instrumentation;

import static java.util.Arrays.asList;
import static java.util.Collections.unmodifiableSet;
import static net.bytebuddy.matcher.ElementMatchers.any;

import io.opentelemetry.javaagent.bootstrap.internal.ExperimentalConfig;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.Ordered;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
import net.bytebuddy.matcher.ElementMatcher;

/**
 * Instrumentation module groups several connected {@link TypeInstrumentation}s together, sharing
 * class loader matcher, helper classes, muzzle safety checks, etc. Ideally all types in a single
 * instrumented library should live in a single module.
 *
 * 

Classes extending {@link InstrumentationModule} should be public and non-final so that it's * possible to extend and reuse them in vendor distributions. * *

{@link InstrumentationModule} is an SPI, you need to ensure that a proper {@code * META-INF/services/} provider file is created for it to be picked up by the agent. See {@link * java.util.ServiceLoader} for more details. */ public abstract class InstrumentationModule implements Ordered { private static final Logger logger = Logger.getLogger(InstrumentationModule.class.getName()); private final Set instrumentationNames; /** * Creates an instrumentation module. Note that all implementations of {@link * InstrumentationModule} must have a default constructor (for SPI), so they have to pass the * instrumentation names to the super class constructor. * *

When enabling or disabling the instrumentation module configuration property that * corresponds to the main instrumentation name is considered first, after that additional * instrumentation names are considered in the order they are listed here. * *

The instrumentation names should follow several rules: * *

    *
  • Instrumentation names should consist of hyphen-separated words, e.g. {@code * instrumented-library}; *
  • In general, instrumentation names should be the as close as possible to the gradle module * name - which in turn should be as close as possible to the instrumented library name; *
  • The main instrumentation name should be the same as the gradle module name, minus the * version if it's a part of the module name. When several versions of a library are * instrumented they should all share the same main instrumentation name so that it's easy * to enable/disable the instrumentation regardless of the runtime library version; *
  • If the gradle module has a version as a part of its name, an additional instrumentation * name containing the version should be passed, e.g. {@code instrumented-library-1.0}. *
*/ protected InstrumentationModule( String mainInstrumentationName, String... additionalInstrumentationNames) { LinkedHashSet names = new LinkedHashSet<>(additionalInstrumentationNames.length + 1); names.add(mainInstrumentationName); names.addAll(asList(additionalInstrumentationNames)); this.instrumentationNames = unmodifiableSet(names); } /** * Returns all instrumentation names assigned to this module. See {@link * #InstrumentationModule(String, String...)} for more details about instrumentation names. */ public final Set instrumentationNames() { return instrumentationNames; } /** * Returns the main instrumentation name. See {@link #InstrumentationModule(String, String...)} * for more details about instrumentation names. */ public final String instrumentationName() { return instrumentationNames.iterator().next(); } /** * Allows instrumentation modules to disable themselves by default, or to additionally disable * themselves on some other condition. */ public boolean defaultEnabled(ConfigProperties config) { return config.getBoolean("otel.instrumentation.common.default-enabled", true); } /** * Instrumentation modules can override this method to specify additional packages (or classes) * that should be treated as "library instrumentation" packages. Classes from those packages will * be treated by muzzle as instrumentation helper classes: they will be scanned for references and * automatically injected into the application class loader if they're used in any type * instrumentation. The classes for which this predicate returns {@code true} will be treated as * helper classes, in addition to the default ones defined in the {@code HelperClassPredicate} * class. * * @param className The name of the class that may or may not be a helper class. */ public boolean isHelperClass(String className) { return false; } /** * Note this is an experimental feature until phase 1 of * implementing the invokedynamic based instrumentation mechanism is complete. Instrumentation * modules that override this to true (recommended) must use the inline=false Invoke Dynamic style * of Byte Buddy advices which calls out to helper classes in their own classloader, thus enabling * better isolation, best practice code development, avoids shading and enables standard debugging * techniques. The non-inlining of advice will be enforced by muzzle (TODO) */ public boolean isIndyModule() { return IndyConfigurationHolder.indyEnabled; } /** Register resource names to inject into the user's class loader. */ public void registerHelperResources(HelperResourceBuilder helperResourceBuilder) {} /** * An instrumentation module can implement this method to make sure that the class loader contains * the particular library version. It is useful to implement that if the muzzle check does not * fail for versions out of the instrumentation's scope. * *

E.g. supposing version 1.0 has class {@code A}, but it was removed in version 2.0; A is not * used in the helper classes at all; this module is instrumenting 2.0: this method will return * {@code not(hasClassesNamed("A"))}. * * @return A type matcher used to match the class loader under transform */ public ElementMatcher.Junction classLoaderMatcher() { return any(); } /** Returns a list of all individual type instrumentation in this module. */ public abstract List typeInstrumentations(); /** * Returns a list of additional instrumentation helper classes, which are not automatically * detected during compile time. * *

If your instrumentation module does not apply and you see warnings about missing classes in * the logs, you may need to override this method and provide fully qualified classes names of * helper classes that your instrumentation uses. * *

These helper classes will be injected into the application class loader after automatically * detected ones. */ public List getAdditionalHelperClassNames() { return Collections.emptyList(); } // InstrumentationModule is loaded before ExperimentalConfig is initialized private static class IndyConfigurationHolder { private static final boolean indyEnabled; static { indyEnabled = ExperimentalConfig.get().indyEnabled(); if (indyEnabled) { logger.info("Enabled indy for instrumentation modules"); } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy