Skywalking process analysis_1 (main process analysis)

Foreword

skywalking As the most commonly used APM monitoring at present, its importance is indescribable. I have also made many customized transformations for the company’s business. Its internal principle design is very clever, especially the custom agentClassLoader class. The loading part is even more worthy of learning from this idea. This series analyzes the internal principles of skywalking to make it easier for everyone to understand the essence of startup.

Version

  • 8.14.0

Features

  • How to use javaagent to start statically
  • Using the bytebuddy framework, the entry point is the permain() method
  • The bytecode can be modified at will when the class is loaded, as long as it meets the specifications.
  • The plug-in customizes the class loader for perfect isolation

Entrance

Under the apm-sniffer/apm-agent module, pom configuration

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <premain.class>org.apache.skywalking.apm.agent.SkyWalkingAgent</premain.class>
    <!-- Omit... -->
</properties>
<plugin>
    <artifactId>maven-shade-plugin</artifactId>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <shadedArtifactAttached>false</shadedArtifactAttached>
                <createDependencyReducedPom>true</createDependencyReducedPom>
                <createSourcesJar>true</createSourcesJar>
                <shadeSourcesContent>true</shadeSourcesContent>
                <transformers>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                        <manifestEntries>
                            <Premain-Class>${premain.class}</Premain-Class>
                            <Can-Redefine-Classes>${can.redefine.classes}</Can-Redefine-Classes>
                            <Can-Retransform-Classes>${can.retransform.classes}</Can-Retransform-Classes>
                        </manifestEntries>
                    </transformer>
                </transformers>
                <!-- Omit... -->
            </configuration>
        </execution>
    </executions>
</plugin>

The entry of org.apache.skywalking.apm.agent.SkyWalkingAgent is specified here. The following is the important premain method.

SkyWalkingAgent.premain

public class SkyWalkingAgent {<!-- -->
private static ILog LOGGER = LogManager.getLogger(SkyWalkingAgent.class);

/**
 * Main entrance. Use byte-buddy transform to enhance all classes, which define in plugins.
 */
public static void premain(String agentArgs, Instrumentation instrumentation) throws PluginException {<!-- -->
    final PluginFinder pluginFinder;
    try {<!-- -->
        //Read the configuration file and core jar
        SnifferConfigInitializer.initializeCoreConfig(agentArgs);
    } catch (Exception e) {<!-- -->
        // try to resolve a new logger, and use the new logger to write the error log here
        LogManager.getLogger(SkyWalkingAgent.class)
                  .error(e, "SkyWalking agent initialized failure. Shutting down.");
        return;
    } finally {<!-- -->
        // refresh logger again after initialization finishes
        LOGGER = LogManager.getLogger(SkyWalkingAgent.class);
    }

    if (!Config.Agent.ENABLE) {<!-- -->
        LOGGER.warn("SkyWalking agent is disabled.");
        return;
    }

    try {<!-- -->
        //Read plug-ins and load plug-ins
        pluginFinder = new PluginFinder(new PluginBootstrap().loadPlugins());
    } catch (AgentPackageNotFoundException ape) {<!-- -->
        LOGGER.error(ape, "Locate agent.jar failure. Shutting down.");
        return;
    } catch (Exception e) {<!-- -->
        LOGGER.error(e, "SkyWalking agent initialized failure. Shutting down.");
        return;
    }
    //Create byteBuddy
    final ByteBuddy byteBuddy = new ByteBuddy().with(TypeValidation.of(Config.Agent.IS_OPEN_DEBUGGING_CLASS));

    //Which classes are ignored?
    AgentBuilder agentBuilder = new AgentBuilder.Default(byteBuddy).ignore(
        nameStartsWith("net.bytebuddy.")
            .or(nameStartsWith("org.slf4j."))
            .or(nameStartsWith("org.groovy."))
            .or(nameContains("javassist"))
            .or(nameContains(".asm."))
            .or(nameContains(".reflectasm."))
            .or(nameStartsWith("sun.reflect"))
            .or(allSkyWalkingAgentExcludeToolkit())
            .or(ElementMatchers.isSynthetic()));

    JDK9ModuleExporter.EdgeClasses edgeClasses = new JDK9ModuleExporter.EdgeClasses();
    try {<!-- -->
        /*
        * There is a key logic in it to inject some classes into the Boosttrap class loader in order to solve the problem that the Bootstrap class loader cannot access the content in the App class loader.
        * */
        agentBuilder = BootstrapInstrumentBoost.inject(pluginFinder, instrumentation, agentBuilder, edgeClasses);
    } catch (Exception e) {<!-- -->
        LOGGER.error(e, "SkyWalking agent inject bootstrap instrumentation failure. Shutting down.");
        return;
    }
    /**
     * Bypass jdk modularity problem
     * */
    try {<!-- -->
        agentBuilder = JDK9ModuleExporter.openReadEdge(instrumentation, agentBuilder, edgeClasses);
    } catch (Exception e) {<!-- -->
        LOGGER.error(e, "SkyWalking agent open read edge in JDK 9 + failure. Shutting down.");
        return;
    }
    //Whether to save the modified bytecode to disk/memory according to configuration
    if (Config.Agent.IS_CACHE_ENHANCED_CLASS) {<!-- -->
        try {<!-- -->
            agentBuilder = agentBuilder.with(new CacheableTransformerDecorator(Config.Agent.CLASS_CACHE_MODE));
            LOGGER.info("SkyWalking agent class cache [{}] activated.", Config.Agent.CLASS_CACHE_MODE);
        } catch (Exception e) {<!-- -->
            LOGGER.error(e, "SkyWalking agent can't active class cache.");
        }
    }

    agentBuilder.type(pluginFinder.buildMatch())//Specify the class to be intercepted by byteBuddy
                .transform(new Transformer(pluginFinder))//Specify bytecode enhancement tool
                .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)//The difference between redefine and retransformation is whether to retain the content before modification
                .with(new RedefinitionListener())
                .with(new Listener())
                .installOn(instrumentation);//Install agent to instrumentation

    PluginFinder.pluginInitCompleted();

    try {<!-- -->
        //Start service
        //Start these services of BootService in sequence
        ServiceManager.INSTANCE.boot();
    } catch (Exception e) {<!-- -->
        LOGGER.error(e, "Skywalking agent boot failure.");
    }
    //Close these services of BootService in sequence
    Runtime.getRuntime()
           .addShutdownHook(new Thread(ServiceManager.INSTANCE::shutdown, "skywalking service shutdown thread"));
}

The general process of premain is

  • Read configuration file
  • Load plugin
  • Create byteBuddy
  • Make some extra preparations
  • Start service
  • Turn off service hook

In future series, we will gradually analyze the process of each stage.