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

co.elastic.apm.attach.ElasticApmAttacher Maven / Gradle / Ivy

The newest version!
/*
 * Licensed to Elasticsearch B.V. under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Elasticsearch B.V. licenses this file to you 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 co.elastic.apm.attach;

import co.elastic.apm.agent.common.util.ResourceExtractionUtil;
import co.elastic.apm.agent.common.util.SystemStandardOutputLogger;
import co.elastic.apm.attach.bytebuddy.agent.ByteBuddyAgent;

import javax.annotation.Nullable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * Attaches the Elastic Apm agent to the current or a remote JVM
 */
public class ElasticApmAttacher {

    /**
     * This key is very short on purpose.
     * The longer the agent argument ({@code -javaagent:=}), the greater the chance that the max length of the agent argument is reached.
     * Because of a bug in the {@linkplain ByteBuddyAgent.AttachmentProvider.ForEmulatedAttachment emulated attachment},
     * this can even lead to segfaults.
     */
    private static final String TEMP_PROPERTIES_FILE_KEY = "c";

    /**
     * Attaches the Elastic Apm agent to the current JVM.
     * 

* This method may only be invoked once. *

*

* Tries to load {@code elasticapm.properties} from the classpath, if exists. *

* * @throws IllegalStateException if there was a problem while attaching the agent to this VM */ public static void attach() { attach(loadPropertiesFromClasspath("elasticapm.properties")); } /** * Attaches the Elastic Apm agent to the current JVM. *

* This method may only be invoked once. *

* * @param propertiesLocation the location within the classpath which contains the agent configuration properties file * @throws IllegalStateException if there was a problem while attaching the agent to this VM * @since 1.11.0 */ public static void attach(String propertiesLocation) { attach(loadPropertiesFromClasspath(propertiesLocation)); } private static Map loadPropertiesFromClasspath(String propertiesLocation) { Map propertyMap = new HashMap<>(); final Properties props = new Properties(); try (InputStream resourceStream = ElasticApmAttacher.class.getClassLoader().getResourceAsStream(propertiesLocation)) { if (resourceStream != null) { props.load(resourceStream); for (String propertyName : props.stringPropertyNames()) { propertyMap.put(propertyName, props.getProperty(propertyName)); } } } catch (IOException e) { SystemStandardOutputLogger.printStackTrace(e); } return propertyMap; } /** * Attaches the Elastic Apm agent to the current JVM. *

* This method may only be invoked once. *

* * @param configuration the agent configuration * @throws IllegalStateException if there was a problem while attaching the agent to this VM */ public static void attach(Map configuration) { // optimization, this is checked in AgentMain#init again if (Boolean.getBoolean("ElasticApm.attached")) { return; } attach(ByteBuddyAgent.ProcessProvider.ForCurrentVm.INSTANCE.resolve(), configuration); } /** * Store configuration to a temporary file * * @param configuration agent configuration * @param folder temporary folder, use {@literal null} to use default * @return created file if any, {@literal null} if none was created */ static File createTempProperties(Map configuration, @Nullable File folder) { File tempFile = null; if (!configuration.isEmpty()) { Properties properties = new Properties(); properties.putAll(configuration); try { tempFile = File.createTempFile("elstcapm", ".tmp", folder); try (FileOutputStream outputStream = new FileOutputStream(tempFile)) { properties.store(outputStream, null); } } catch (IOException e) { SystemStandardOutputLogger.printStackTrace(e); } } return tempFile; } /** * Attaches the agent to a remote JVM * * @param pid the PID of the JVM the agent should be attached on * @param configuration the agent configuration */ public static void attach(String pid, Map configuration) { attach(pid, configuration, AgentJarFileHolder.INSTANCE.agentJarFile); } /** * Attaches the agent to a remote JVM * * @param pid the PID of the JVM the agent should be attached on * @param configuration the agent configuration * @param agentJarFile the agent jar file */ public static void attach(String pid, Map configuration, File agentJarFile) { // making a copy of provided configuration as user might have used an immutable map impl. // and guard against user-provided null keys and values Map config = new HashMap<>(); for (Map.Entry entry : configuration.entrySet()) { if (entry.getKey() != null && entry.getValue() != null) { config.put(entry.getKey(), entry.getValue()); } } if (!config.containsKey("activation_method")) { config.put("activation_method", "PROGRAMMATIC_SELF_ATTACH"); } File tempFile = createTempProperties(config, null); String agentArgs = tempFile == null ? null : TEMP_PROPERTIES_FILE_KEY + "=" + tempFile.getAbsolutePath(); attachWithFallback(agentJarFile, pid, agentArgs); if (tempFile != null) { if (!tempFile.delete()) { tempFile.deleteOnExit(); } } } private static void attachWithFallback(File agentJarFile, String pid, String agentArgs) { try { // while the native providers may report to be supported and appear to work properly, in practice there are // cases (Docker without '--init' option on some JDK images like 'openjdk:8-jdk-alpine') where the accessor // returned by the provider will not work as expected at attachment time. ByteBuddyAgent.attach(agentJarFile, pid, agentArgs, ElasticAttachmentProvider.get()); } catch (RuntimeException e1) { try { ByteBuddyAgent.attach(agentJarFile, pid, agentArgs, ElasticAttachmentProvider.getFallback()); } catch (RuntimeException e2) { // output the two exceptions for debugging SystemStandardOutputLogger.stdErrInfo("Unable to attach with fallback provider:"); SystemStandardOutputLogger.printStackTrace(e2); SystemStandardOutputLogger.stdErrInfo("Unable to attach with regular provider:"); SystemStandardOutputLogger.printStackTrace(e1); } } } /** * Attaches the agent to a remote JVM * * @param pid the PID of the JVM the agent should be attached on * @param agentArgs the agent arguments * @deprecated use {@link #attach(String, Map)} */ @Deprecated public static void attach(String pid, String agentArgs) { attachWithFallback(AgentJarFileHolder.INSTANCE.agentJarFile, pid, agentArgs); } public static File getBundledAgentJarFile() { return AgentJarFileHolder.INSTANCE.agentJarFile; } private enum AgentJarFileHolder { INSTANCE; // initializes lazily and ensures its only loaded once final File agentJarFile = getAgentJarFile(); private static File getAgentJarFile() { if (ElasticApmAttacher.class.getResource("/elastic-apm-agent.jar") != null) { // packaged agent as resource return ResourceExtractionUtil.extractResourceToTempDirectory("elastic-apm-agent.jar", "elastic-apm-agent", ".jar").toFile(); } // Running attacher without proper packaging is quite common when running it from the IDE without having // it packaged from CLI beforehand throw new IllegalStateException("unable to get packaged agent within attacher jar."); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy