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

com.undefinedlabs.scope.ScopeAgent Maven / Gradle / Ivy

Go to download

Scope is a APM for tests to give engineering teams unprecedented visibility into their CI process to quickly identify, troubleshoot and fix failed builds. The Scope Java agent allows to instrument the tests executions and send the results to your Scope instance.

There is a newer version: 0.15.1-beta.2
Show newest version
package com.undefinedlabs.scope;

import com.undefinedlabs.scope.bootstrap.ContextStrategy;
import com.undefinedlabs.scope.bootstrap.ContextTrampoline;
import com.undefinedlabs.scope.coverage.GlobalCoverageReporter;
import com.undefinedlabs.scope.coverage.instrumentation.CoverageAgentBuilderCreator;
import com.undefinedlabs.scope.coverage.reporter.impl.CoverageReporterImplFactory;
import com.undefinedlabs.scope.coverage.rules.ScopeCoverageAgentRule;
import com.undefinedlabs.scope.initializers.Initializer;
import com.undefinedlabs.scope.initializers.InitializersResolver;
import com.undefinedlabs.scope.initializers.ScopeInitializersResolver;
import com.undefinedlabs.scope.logger.ScopeLogger;
import com.undefinedlabs.scope.logger.ScopeLoggerResolver;
import com.undefinedlabs.scope.rules.*;
import com.undefinedlabs.scope.runner.ScopeGlobalRunner;
import com.undefinedlabs.scope.runner.ScopeRunnerFilter;
import com.undefinedlabs.scope.runner.impl.ScopeRunnerImplFactory;
import com.undefinedlabs.scope.runner.rules.ScopeRunnerAgentRule;
import com.undefinedlabs.scope.settings.ScopeSettings;
import com.undefinedlabs.scope.settings.ScopeSettingsResolver;
import com.undefinedlabs.scope.settings.credentials.CredentialsUtils;
import com.undefinedlabs.scope.settings.credentials.ScopeCredentials;
import com.undefinedlabs.scope.settings.remote.ScopeRemoteSettings;
import com.undefinedlabs.scope.settings.remote.ScopeRemoteSettingsResolver;
import com.undefinedlabs.scope.statistics.Statistics;
import com.undefinedlabs.scope.utils.ResourceUtils;
import com.undefinedlabs.scope.utils.StringUtils;
import com.undefinedlabs.scope.utils.props.SystemProps;
import com.undefinedlabs.scope.utils.reports.ReportURLUtils;
import io.opentracing.Tracer;
import io.opentracing.util.GlobalTracer;
import net.bytebuddy.agent.builder.AgentBuilder;

import java.lang.instrument.Instrumentation;
import java.util.jar.JarFile;

public class ScopeAgent {

    private static final ScopeLogger LOG = ScopeLoggerResolver.INSTANCE.get();

    private final TracerRegister tracerRegisterAsGlobalTracer;
    private final Tracer tracer;
    private final ScopeSettings settings;
    private final ScopeRemoteSettings remoteSettings;
    private final InitializersResolver initializersResolver;
    private final AgentRulesResolver agentRulesResolver;
    private final CoverageAgentRulesResolver coverageAgentRulesResolver;
    private final RunnerAgentRulesResolver runnerAgentRulesResolver;
    private final AgentBuilder coverageInstrumentation;
    private final Instrumentation instrumentation;
    private static Boolean initialized = Boolean.FALSE;

    public static void premain(final String arg, final Instrumentation instrumentation) throws Exception {
        synchronized (initialized) {
            if (!initialized) {

                instrumentation.appendToBootstrapClassLoaderSearch(new JarFile(ResourceUtils.INSTANCE.getResourceAsTempFile("bootstrap.jar")));

                checkLoadedByBootstrapClassloader(ContextStrategy.class);
                checkLoadedByBootstrapClassloader(ContextTrampoline.class);

                final ScopeSettings settings = ScopeSettingsResolver.INSTANCE.get();
                final ScopeRemoteSettings remoteSettings = ScopeRemoteSettingsResolver.INSTANCE.get(settings);
                settings.printSettings(LOG);
                remoteSettings.printSettings(LOG);

                final ScopeAgent scopeAgent = new ScopeAgent(
                        ScopeTracerCreator.INSTANCE,
                        ScopeTracerRegisterAsGlobalTracer.INSTANCE,
                        ScopeInitializersResolver.INSTANCE,
                        ScopeAgentRulesResolver.INSTANCE,
                        ScopeCoverageAgentRulesResolver.INSTANCE,
                        ScopeRunnerAgentRulesResolver.INSTANCE,
                        settings,
                        remoteSettings,
                        CoverageAgentBuilderCreator.INSTANCE.create(),
                        instrumentation);

                final Tracer localTracer = scopeAgent.execute();
                System.setProperty(ScopeSettings.SCOPE_AGENT_IS_EXECUTING, "true");
                final String logPathFile = Statistics.INSTANCE.getLogPathFile();

                if (localTracer != null) {
                    LOG.info("** ScopeAgent started [ " + SystemProps.AGENT_ID + " ] **");
                    Statistics.INSTANCE.registerStartScopeExecution();

                    Runtime.getRuntime().addShutdownHook(new Thread() {
                        public void run() {
                            final boolean mustSetGlobalTracer = (boolean) settings.getSetting(ScopeSettings.SCOPE_TRACER_GLOBAL);
                            final boolean isAutoInstrument = (boolean) settings.getSetting(ScopeSettings.SCOPE_INSTRUMENTATION_ENABLED);
                            final boolean testingMode = (boolean) settings.getSetting(ScopeSettings.SCOPE_TESTING_MODE);


                            if (mustSetGlobalTracer && isAutoInstrument) {
                                ScopeGlobalTracer.get().close();
                            } else {
                                localTracer.close();
                            }

                            Statistics.INSTANCE.registerFinishScopeExecution();
                            final String statisticsReport = Statistics.INSTANCE.getReport();
                            final int totalSent = Statistics.INSTANCE.getTotalSentSpans().get();
                            final int failedSent = Statistics.INSTANCE.getFailedSentSpans().get();
                            final int notFinished = Statistics.INSTANCE.getNotFinishedSpansNum();

                            if (testingMode && totalSent > 0) {
                                final ScopeCredentials credentials = CredentialsUtils.INSTANCE.extractFromDSN((String) ScopeSettingsResolver.INSTANCE.get().getSetting(ScopeSettings.SCOPE_DSN));

                                System.out.println("\n** Scope Test Report **\n");

                                if (failedSent == 0 && notFinished == 0) {
                                    System.out.println("Access the detailed test report for this build at:");
                                    System.out.println("\t" + ReportURLUtils.INSTANCE.getReportURL(credentials.getApiEndpoint(), SystemProps.AGENT_ID));
                                } else if (failedSent > 0 && notFinished == 0) {
                                    System.out.println("There was a problem sending data to Scope.");
                                    if (totalSent > failedSent) {
                                        System.out.println("Partial results for this build are available at:");
                                        System.out.println("\t" + ReportURLUtils.INSTANCE.getReportURL(credentials.getApiEndpoint(), SystemProps.AGENT_ID));
                                    }
                                    System.out.println("Check the agent logs at " + logPathFile + " for more information.\n\n");
                                } else if (failedSent == 0 && notFinished > 0) {
                                    System.out.println("There was a problem capturing data from Scope Agent.");
                                    System.out.println("Partial results for this build can be available at:");
                                    System.out.println("\t" + ReportURLUtils.INSTANCE.getReportURL(credentials.getApiEndpoint(), SystemProps.AGENT_ID));
                                    System.out.println("Check the agent logs at " + logPathFile + " for more information.\n\n");
                                }
                            }

                            LOG.info("** ScopeAgent finished [ " + SystemProps.AGENT_ID + " ] **");
                            LOG.debug(statisticsReport);
                        }
                    });
                } else {

                    if (StringUtils.isEmpty((String) ScopeSettingsResolver.INSTANCE.get().getSetting(ScopeSettings.SCOPE_DSN))) {
                        System.out.println("SCOPE_DSN not defined, test results will not be sent to Scope.");
                        System.out.println("Developing locally? Try out Scratchpad! https://app.scope.dev/local-dev/");
                    } else {
                        System.out.println("ScopeAgent could not be started successfully.");
                        System.out.println("Check the agent logs at " + logPathFile + " for more information.\n\n");
                    }
                }


                initialized = Boolean.TRUE;
            }
        }
    }

    protected ScopeAgent(final TracerCreator tracerCreator,
                         final TracerRegister tracerRegisterAsGlobalTracer,
                         final InitializersResolver initializersResolver,
                         final AgentRulesResolver agentRulesResolver,
                         final CoverageAgentRulesResolver coverageAgentRulesResolver,
                         final RunnerAgentRulesResolver runnerAgentRulesResolver,
                         final ScopeSettings settings,
                         final ScopeRemoteSettings remoteSettings,
                         final AgentBuilder coverageInstrumentation,
                         final Instrumentation instrumentation) {
        this.tracerRegisterAsGlobalTracer = tracerRegisterAsGlobalTracer;
        this.tracer = tracerCreator.create(settings);
        this.settings = settings;
        this.remoteSettings = remoteSettings;
        this.initializersResolver = initializersResolver;
        this.agentRulesResolver = agentRulesResolver;
        this.coverageAgentRulesResolver = coverageAgentRulesResolver;
        this.runnerAgentRulesResolver = runnerAgentRulesResolver;
        this.coverageInstrumentation = coverageInstrumentation;
        this.instrumentation = instrumentation;
    }

    protected Tracer execute() {
        if (this.tracer == null) {
            return null;
        }

        ScopeGlobalTracer.registerIfAbsent(this.tracer);

        final String branch = (String) this.settings.getSetting(ScopeSettings.SCOPE_BRANCH);
        Statistics.INSTANCE.registerBranch(branch);

        final boolean mustSetGlobalTracer = (boolean) this.settings.getSetting(ScopeSettings.SCOPE_TRACER_GLOBAL);
        final boolean isAutoInstrument = (boolean) this.settings.getSetting(ScopeSettings.SCOPE_INSTRUMENTATION_ENABLED);

        try {
            final boolean mustInstrumentForRunner = ScopeRunnerFilter.INSTANCE.mustApplyRunner(this.settings);
            Statistics.INSTANCE.registerRunnerActivation(mustInstrumentForRunner);

            if(mustInstrumentForRunner) {
                final boolean runnerRegistered = ScopeGlobalRunner.registerIfAbsent(ScopeRunnerImplFactory.INSTANCE.create(this.remoteSettings));
                if (!runnerRegistered) {
                    LOG.warn("Scope Runner could not be registered as ScopeGlobalRunner. Registered runner as Global: " + ScopeGlobalRunner.get());
                } else {
                    for(final ScopeRunnerAgentRule rule : this.runnerAgentRulesResolver.resolve()) {
                        LOG.debug("--- Installed " + rule);
                        rule.installOn(this.instrumentation);
                    }
                }
            }
        } catch (final IllegalArgumentException e) {
            Statistics.INSTANCE.registerRunnerActivation(false);
            LOG.error(e.getMessage());
        }

        final boolean mustInstrumentForCoverage = (boolean) this.settings.getSetting(ScopeSettings.SCOPE_CODE_PATH_ENABLED);
        Statistics.INSTANCE.registerCoverageActivation(mustInstrumentForCoverage);

        if (mustInstrumentForCoverage) {
            final boolean coverageReporterRegistered = GlobalCoverageReporter.registerIfAbsent(CoverageReporterImplFactory.INSTANCE.create(this.settings));
            if (!coverageReporterRegistered) {
                LOG.warn("Scope CoverageReporter could not be registered as GlobalCoverageReporter. Registered reporter as Global: " + GlobalCoverageReporter.get());
            } else {
                this.coverageInstrumentation.installOn(this.instrumentation);
                for (final ScopeCoverageAgentRule rule : this.coverageAgentRulesResolver.resolve()) {
                    LOG.debug("--- Installed " + rule);
                    rule.installOn(this.instrumentation);
                }
            }
        }

        if (mustSetGlobalTracer) {
            if (!this.tracerRegisterAsGlobalTracer.registerAsGlobalTracer(this.tracer)) {
                LOG.warn("ScopeTracer could not be registered as GlobalTracer. Registered tracer as Global: " + GlobalTracer.get());
            }
        }


        if (isAutoInstrument) {
            for (final Initializer initializer : this.initializersResolver.resolve()) {
                LOG.debug("--- Executed " + initializer);
                initializer.run();
            }

            for (final ScopeAgentRule rule : this.agentRulesResolver.resolve()) {
                LOG.debug("--- Installed " + rule);
                rule.installOn(this.instrumentation);
            }
        }


        return this.tracer;
    }

    private static void checkLoadedByBootstrapClassloader(Class clazz) {
        if (clazz.getClassLoader() != null) {
            throw new RuntimeException(clazz.getName() + " must be loaded by bootstrap classloader");
        }
    }

    public static void agentmain(String arg, Instrumentation instrumentation) throws Exception {
        premain(arg, instrumentation);
    }


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy