org.glowroot.agent.MainEntryPoint Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of glowroot-agent-it-harness Show documentation
Show all versions of glowroot-agent-it-harness Show documentation
Glowroot Agent Integration Test Harness
/*
* Copyright 2011-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.glowroot.agent;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.instrument.Instrumentation;
import java.lang.management.ManagementFactory;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.ListIterator;
import java.util.Locale;
import java.util.Map;
import java.util.jar.JarFile;
import org.glowroot.agent.shaded.org.glowroot.agent.it.harness.shaded.com.google.common.base.Joiner;
import org.glowroot.agent.shaded.org.glowroot.agent.it.harness.shaded.com.google.common.base.Objects;
import org.glowroot.agent.shaded.org.glowroot.agent.it.harness.shaded.com.google.common.base.StandardSystemProperty;
import org.glowroot.agent.shaded.org.glowroot.agent.it.harness.shaded.com.google.common.base.Strings;
import org.glowroot.agent.shaded.org.glowroot.agent.it.harness.shaded.com.google.common.collect.ImmutableMap;
import org.glowroot.agent.shaded.org.glowroot.agent.it.harness.shaded.com.google.common.collect.Lists;
import org.glowroot.agent.shaded.org.glowroot.agent.it.harness.shaded.com.google.common.collect.Maps;
import org.glowroot.agent.shaded.org.checkerframework.checker.nullness.qual.EnsuresNonNull;
import org.glowroot.agent.shaded.org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.glowroot.agent.shaded.org.checkerframework.checker.nullness.qual.Nullable;
import org.glowroot.agent.shaded.org.checkerframework.checker.nullness.qual.RequiresNonNull;
import org.glowroot.agent.shaded.org.glowroot.agent.shaded.org.slf4j.Logger;
import org.glowroot.agent.shaded.org.glowroot.agent.shaded.org.slf4j.LoggerFactory;
import org.glowroot.agent.bytecode.api.BytecodeServiceHolder;
import org.glowroot.agent.collector.Collector;
import org.glowroot.agent.init.AgentModule;
import org.glowroot.agent.init.GlowrootAgentInit;
import org.glowroot.agent.init.GlowrootAgentInitFactory;
import org.glowroot.agent.init.NonEmbeddedGlowrootAgentInit;
import org.glowroot.agent.init.PreCheckLoadedClasses.PreCheckClassFileTransformer;
import org.glowroot.agent.util.JavaVersion;
import org.glowroot.agent.weaving.Java9;
import org.glowroot.agent.shaded.org.glowroot.common.util.OnlyUsedByTests;
import org.glowroot.agent.shaded.org.glowroot.common.util.Version;
import static org.glowroot.agent.shaded.org.glowroot.agent.it.harness.shaded.com.google.common.base.Charsets.UTF_8;
import static org.glowroot.agent.shaded.org.glowroot.agent.it.harness.shaded.com.google.common.base.Preconditions.checkNotNull;
public class MainEntryPoint {
private static final boolean PRE_CHECK_LOADED_CLASSES =
Boolean.getBoolean("glowroot.debug.preCheckLoadedClasses");
public static final boolean PRINT_CLASS_LOADING =
Boolean.getBoolean("glowroot.debug.printClassLoading");
// need to wait to init logger until after establishing logDir
private static volatile @MonotonicNonNull Logger startupLogger;
@OnlyUsedByTests
private static @MonotonicNonNull GlowrootAgentInit glowrootAgentInit;
private MainEntryPoint() {}
public static void premain(Instrumentation instrumentation, Class>[] allPriorLoadedClasses,
@Nullable File glowrootJarFile) {
if (startupLogger != null) {
// glowroot is already running, probably due to multiple glowroot -javaagent JVM args
return;
}
// DO NOT USE ANY GUAVA CLASSES before initLogging() because they trigger loading of jul
// (and thus org.glowroot.agent.jul.Logger and thus glowroot's shaded slf4j)
PreCheckClassFileTransformer preCheckClassFileTransformer = null;
Directories directories;
try {
if (PRE_CHECK_LOADED_CLASSES) {
preCheckClassFileTransformer = new PreCheckClassFileTransformer();
instrumentation.addTransformer(preCheckClassFileTransformer);
}
if (PRINT_CLASS_LOADING) {
DebuggingClassFileTransformer transformer = new DebuggingClassFileTransformer();
instrumentation.addTransformer(transformer, true);
instrumentation
.retransformClasses(Class.forName("sun.misc.Launcher$AppClassLoader"));
instrumentation.removeTransformer(transformer);
}
directories = new Directories(glowrootJarFile);
// init logger as early as possible
initLogging(directories.getConfDirs(), directories.getLogDir(),
directories.getLoggingLogstashJarFile(), instrumentation);
PreCheckClassFileTransformer.initLogger();
DebuggingClassFileTransformer.initLogger();
if (directories.logStartupErrorMultiDirWithMissingAgentId()) {
startupLogger
.error("Glowroot failed to start: multi.dir is true, but missing agent.id");
return;
}
if (directories.getAgentDirLockCloseable() == null) {
ImmutableMap properties =
getGlowrootProperties(directories.getConfDirs());
logAgentDirsLockedException(directories.getConfDir(),
new File(directories.getTmpDir(), ".lock"), properties);
return;
}
} catch (Throwable t) {
// log error but don't re-throw which would prevent monitored app from starting
// also, don't use logger since not initialized yet
System.err.println("Glowroot failed to start: " + t.getMessage());
t.printStackTrace();
return;
}
if (PRE_CHECK_LOADED_CLASSES) {
if (AgentModule.logAnyImportantClassLoadedPriorToWeavingInit(allPriorLoadedClasses,
glowrootJarFile, true)) {
List classNames = Lists.newArrayList();
for (Class> clazz : allPriorLoadedClasses) {
String className = clazz.getName();
if (!className.startsWith("[")) {
classNames.add(className);
}
}
Collections.sort(classNames);
startupLogger.warn("PRE-CHECK: full list of classes already loaded: {}",
Joiner.on(", ").join(classNames));
} else {
startupLogger.info("PRE-CHECK: successful");
}
}
try {
instrumentation.addTransformer(new ManagementFactoryHackClassFileTransformer());
// need to load ThreadMXBean before it's possible to start any transactions since
// starting transactions depends on ThreadMXBean and so can lead to problems
// (e.g. see FileInstrumentationPresentAtStartupIT)
ManagementFactory.getThreadMXBean();
// don't remove transformer in case the class is retransformed later
if (JavaVersion.isGreaterThanOrEqualToJava9()) {
Object baseModule = Java9.getModule(ClassLoader.class);
Java9.grantAccessToGlowroot(instrumentation, baseModule);
Java9.grantAccess(instrumentation, "org.glowroot.agent.weaving.ClassLoaders",
"java.lang.ClassLoader", false);
Java9.grantAccess(instrumentation, "org.glowroot.agent.shaded.org.glowroot.agent.it.harness.shaded.io.netty.util.internal.ReflectionUtil",
"java.nio.DirectByteBuffer", false);
Java9.grantAccess(instrumentation, "org.glowroot.agent.shaded.org.glowroot.agent.it.harness.shaded.io.netty.util.internal.ReflectionUtil",
"sun.nio.ch.SelectorImpl", false);
instrumentation.addTransformer(new Java9HackClassFileTransformer());
Class.forName("org.glowroot.agent.weaving.WeavingClassFileTransformer");
// don't remove transformer in case the class is retransformed later
}
if (JavaVersion.isJ9Jvm() && JavaVersion.isJava6()) {
instrumentation.addTransformer(new IbmJ9Java6HackClassFileTransformer());
Class.forName("org.glowroot.agent.shaded.org.glowroot.agent.it.harness.shaded.com.google.protobuf.UnsafeUtil");
// don't remove transformer in case the class is retransformed later
}
ImmutableMap properties =
getGlowrootProperties(directories.getConfDirs());
start(directories, properties, instrumentation, preCheckClassFileTransformer);
} catch (Throwable t) {
// log error but don't re-throw which would prevent monitored app from starting
startupLogger.error("Glowroot failed to start: {}", t.getMessage(), t);
BytecodeServiceHolder.setGlowrootFailedToStart();
}
}
public static void runOfflineViewer(Directories directories,
GlowrootAgentInitFactory glowrootAgentInitFactory) {
// initLogging() already called by OfflineViewer.main()
checkNotNull(startupLogger);
try {
String version = Version.getVersion(MainEntryPoint.class);
startupLogger.info("Glowroot version: {}", version);
startupLogger.info("Java version: {}", getJavaVersion());
ImmutableMap properties =
getGlowrootProperties(directories.getConfDirs());
glowrootAgentInitFactory.newGlowrootAgentInit(directories.getDataDir(), true, null)
.init(directories.getPluginsDir(), directories.getConfDirs(),
directories.getLogDir(), directories.getTmpDir(),
directories.getGlowrootJarFile(), properties, null, null, version,
checkNotNull(directories.getAgentDirLockCloseable()));
} catch (Throwable t) {
startupLogger.error("Glowroot cannot start: {}", t.getMessage(), t);
return;
}
}
@EnsuresNonNull("startupLogger")
public static void initLogging(List confDirs, File logDir,
@Nullable File loggingLogstashJarFile, @Nullable Instrumentation instrumentation)
throws IOException {
if (loggingLogstashJarFile != null && instrumentation != null) {
instrumentation
.appendToBootstrapClassLoaderSearch(new JarFile(loggingLogstashJarFile));
}
if (JavaVersion.isJava6() && "IBM J9 VM".equals(System.getProperty("java.vm.name"))
&& instrumentation != null) {
instrumentation.addTransformer(new IbmJ9Java6HackClassFileTransformer2());
}
for (File confDir : confDirs) {
File logbackXmlOverride = new File(confDir, "glowroot.logback.xml");
if (logbackXmlOverride.exists()) {
System.setProperty("glowroot.logback.configurationFile",
logbackXmlOverride.getAbsolutePath());
break;
}
}
String priorProperty = System.getProperty("glowroot.log.dir");
System.setProperty("glowroot.log.dir", logDir.getPath());
ClassLoader priorLoader = Thread.currentThread().getContextClassLoader();
// setting the context class loader to only load from bootstrap class loader (by specifying
// null parent class loader), otherwise logback will pick up and use a SAX parser on the
// system classpath because SAXParserFactory.newInstance() checks the thread context class
// loader for resource named META-INF/services/javax.xml.parsers.SAXParserFactory
// (see the xerces dependency in glowroot-agent-integration-tests for testing this)
Thread.currentThread().setContextClassLoader(new ClassLoader(null) {
// overriding getResourceAsStream() is needed for JDK 6 since it still manages to
// fallback and find the resource on the system class path otherwise
@Override
public @Nullable InputStream getResourceAsStream(String name) {
if (name.equals("META-INF/services/javax.xml.parsers.SAXParserFactory")) {
return new ByteArrayInputStream(new byte[0]);
}
return null;
}
});
try {
startupLogger = LoggerFactory.getLogger("org.glowroot");
} finally {
Thread.currentThread().setContextClassLoader(priorLoader);
if (priorProperty == null) {
System.clearProperty("glowroot.log.dir");
} else {
System.setProperty("glowroot.log.dir", priorProperty);
}
System.clearProperty("glowroot.logback.configurationFile");
// don't remove transformer in case the class is retransformed later
}
// TODO report checker framework issue that occurs without checkNotNull
checkNotNull(startupLogger);
}
@RequiresNonNull("startupLogger")
private static void start(Directories directories, Map properties,
@Nullable Instrumentation instrumentation,
@Nullable PreCheckClassFileTransformer preCheckClassFileTransformer) throws Exception {
String version = Version.getVersion(MainEntryPoint.class);
startupLogger.info("Glowroot version: {}", version);
startupLogger.info("Java version: {}", getJavaVersion());
startupLogger.info("Java args: {}", getJvmArgs());
glowrootAgentInit = createGlowrootAgentInit(directories, properties, instrumentation);
glowrootAgentInit.init(directories.getPluginsDir(), directories.getConfDirs(),
directories.getLogDir(), directories.getTmpDir(), directories.getGlowrootJarFile(),
properties, instrumentation, preCheckClassFileTransformer, version,
checkNotNull(directories.getAgentDirLockCloseable()));
}
@RequiresNonNull("startupLogger")
private static GlowrootAgentInit createGlowrootAgentInit(Directories directories,
Map properties, @Nullable Instrumentation instrumentation)
throws Exception {
String collectorAddress = properties.get("glowroot.collector.address");
Class extends Collector> customCollectorClass =
loadCustomCollectorClass(directories.getGlowrootDir());
Constructor extends Collector> collectorProxyConstructor = null;
if (customCollectorClass != null) {
try {
collectorProxyConstructor = customCollectorClass.getConstructor(Collector.class);
} catch (NoSuchMethodException e) {
startupLogger.debug(e.getMessage(), e);
}
}
if (customCollectorClass != null && collectorProxyConstructor == null) {
// non-delegating custom class loader
startupLogger.info("using collector: {}", customCollectorClass.getName());
return new NonEmbeddedGlowrootAgentInit(null, null, customCollectorClass);
}
if (collectorAddress == null) {
File embeddedCollectorJarFile = directories.getEmbeddedCollectorJarFile();
ClassLoader embeddedLoader;
if (embeddedCollectorJarFile == null) {
embeddedLoader = ClassLoader.getSystemClassLoader();
} else {
embeddedLoader =
new URLClassLoader(new URL[] {embeddedCollectorJarFile.toURI().toURL()},
ClassLoader.getSystemClassLoader());
}
Class> factoryClass;
try {
factoryClass = Class.forName(
"org.glowroot.agent.embedded.init.EmbeddedGlowrootAgentInitFactory", true,
embeddedLoader);
} catch (ClassNotFoundException e) {
if (embeddedCollectorJarFile == null) {
startupLogger.error("missing lib/glowroot-embedded-collector.jar");
}
throw e;
}
GlowrootAgentInitFactory glowrootAgentInitFactory =
(GlowrootAgentInitFactory) factoryClass.newInstance();
return glowrootAgentInitFactory.newGlowrootAgentInit(directories.getDataDir(), false,
customCollectorClass);
}
if (collectorAddress.startsWith("https://") && instrumentation != null) {
String normalizedOsName = getNormalizedOsName();
if (normalizedOsName == null) {
throw new IllegalStateException("HTTPS connection to central collector is only"
+ " supported on linux, windows and osx, detected os.name: "
+ System.getProperty("os.name"));
}
File centralCollectorHttpsJarFile =
directories.getCentralCollectorHttpsJarFile(normalizedOsName);
if (centralCollectorHttpsJarFile == null) {
throw new IllegalStateException("Missing lib/glowroot-central-collector-https-"
+ normalizedOsName + ".jar");
}
instrumentation
.appendToBootstrapClassLoaderSearch(new JarFile(centralCollectorHttpsJarFile));
// also need to add to system class loader, otherwise native libraries under
// META-INF/native are not found
instrumentation
.appendToSystemClassLoaderSearch(new JarFile(centralCollectorHttpsJarFile));
}
String collectorAuthority = properties.get("glowroot.collector.authority");
return new NonEmbeddedGlowrootAgentInit(collectorAddress, collectorAuthority,
customCollectorClass);
}
private static ImmutableMap getGlowrootProperties(List confDirs)
throws IOException {
Map properties = Maps.newHashMap();
// iterate in reverse, so more specific conf dirs overlay on top of more general conf dirs
ListIterator i = confDirs.listIterator(confDirs.size());
while (i.hasPrevious()) {
PropertiesFiles.upgradeIfNeededAndLoadInto(i.previous(), properties);
}
for (Map.Entry