Skip to content

Commit

Permalink
Merge branch 'main' into apm-agent-java/issues/2976
Browse files Browse the repository at this point in the history
* main:
  make the Agent.activationMethod field immutable (elastic#3032)
  Add Activation method for telemetry on how the agent was started (elastic#2926)
  Unnest exceptions before filtering (elastic#3025)
  Use composite action for updatecli workflow (elastic#3023)

# Conflicts:
#	CHANGELOG.asciidoc
  • Loading branch information
markush81 committed Feb 23, 2023
2 parents d297ea0 + 557f1c7 commit 101cd83
Show file tree
Hide file tree
Showing 21 changed files with 831 additions and 33 deletions.
16 changes: 7 additions & 9 deletions .github/workflows/update-specs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,21 @@ on:
- cron: '0 6 * * *'

permissions:
pull-requests: write
contents: write
contents: read

jobs:
bump:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Git
uses: elastic/apm-pipeline-library/.github/actions/setup-git@current
- name: Install Updatecli in the runner
uses: updatecli/updatecli-action@453502948b442d7b9a923de7b40cc7ce8628505c
- name: Run Updatecli
- uses: elastic/apm-pipeline-library/.github/actions/updatecli@current
env:
GITHUB_TOKEN: ${{ github.token }}
BRANCH_NAME: ${{ github.ref_name }}
run: updatecli apply --config ./.ci/update-specs.yml
with:
vaultUrl: ${{ secrets.VAULT_ADDR }}
vaultRoleId: ${{ secrets.VAULT_ROLE_ID }}
vaultSecretId: ${{ secrets.VAULT_SECRET_ID }}
pipeline: ./.ci/update-specs.yml
- if: failure()
uses: elastic/apm-pipeline-library/.github/actions/notify-build-status@current
with:
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@ endif::[]
communication - {pull}2996[#2996]
* Add the <<config-profiling-inferred-spans-logging-enabled>> config option to suppress async profiler warning messages - {pull}3002[#3002]
* Added support for OpenTelemetry metrics - {pull}2968[#2968]
* Added agent.activation_method telemetry - {pull}2926[#2926]
[float]
===== Bug fixes
* Fixed used instrumentations printed on shutdown {pull}3001[#3001]
* Prevent potential connection leak on network failure - {pull}2869[#2869]
* Fix for inferred spans where the parent id was also a child id - {pull}2686[#2686]
* Fix context propagation for async 7.x and 8.x Elasticsearch clients - {pull}3015[#3015]
* Fix exceptions filtering based on <<config-ignore-exceptions>> when those are <<config-unnest-exceptions, nested>> - {pull}3025[#3025]
* Fix for `HttpUrlConnection.getResponseCode` capturing an exception, also internally already handled - {pull}3024[#3024]
[[release-notes-1.x]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,9 @@ private void onJvmMatch(JvmInfo jvmInfo) throws Exception {

private boolean attach(JvmInfo jvmInfo) throws Exception {
final Map<String, String> agentArgs = getAgentArgs(jvmInfo);
if (!agentArgs.containsKey("activation_method")) {
agentArgs.put("activation_method", "APM_AGENT_ATTACH_CLI");
}
logger.info("Attaching the Elastic APM agent to {} with arguments {}", jvmInfo, agentArgs);

UserRegistry.User user = jvmInfo.getUser(userRegistry);
Expand Down
10 changes: 10 additions & 0 deletions apm-agent-attach/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,16 @@
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,9 @@ public static void attach(String pid, Map<String, String> configuration) {
* @param agentJarFile the agent jar file
*/
public static void attach(String pid, Map<String, String> configuration, File agentJarFile) {
if (!configuration.containsKey("activation_method")) {
configuration.put("activation_method", "PROGRAMMATIC_SELF_ATTACH");
}
File tempFile = createTempProperties(configuration, null);
String agentArgs = tempFile == null ? null : TEMP_PROPERTIES_FILE_KEY + "=" + tempFile.getAbsolutePath();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* 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 java.io.File;
import java.lang.management.ManagementFactory;
import java.util.HashMap;

/**
* Note this is used for integration testing by the core project,
* so don't delete it without running tests there! (It's here
* to avoid a cyclic dependency in the poms)
*/
public class ExampleSelfAttachAppWithProvidedJar {

public static void main(String[] args) throws InterruptedException {
// Just sleep for 5 minutes then exit
//long pid = ProcessHandle.current().pid(); //java 9+
//Use the old hack - doesn't need to be guaranteed all platforms, it's just for testing
String pidHost = ManagementFactory.getRuntimeMXBean().getName();
long pid = Integer.parseInt(pidHost.substring(0,pidHost.indexOf('@')));
ElasticApmAttacher.attach(""+pid, new HashMap<String,String>(), new File(System.getProperty("ElasticApmAgent.jarfile")));
Thread.sleep(5*60*1000);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* 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.agent.configuration;

public enum ActivationMethod {
// Set explicitly by the process starting the agent
K8S_ATTACH, // https://github.com/elastic/apm-mutating-webhook
AWS_LAMBDA_LAYER, // Only if installed by using layers (as other metadata already identifies lambda)
FLEET, // Fleet using 'java -jar apm-agent-attach-cli.jar ...' to attach (directly or through webhook)
APM_AGENT_ATTACH_CLI, // 'java -jar apm-agent-attach-cli.jar ...' used
PROGRAMMATIC_SELF_ATTACH, // ElasticApmAttacher.attach();
AZURE_FUNCTIONS, //?

//Inferred
JAVAAGENT_FLAG, // -javaagent:... command-line option used
ENV_ATTACH, // JAVA_TOOL_OPTIONS env var used to specify -javaagent option

UNKNOWN;

public String toReferenceString() {
return toString().replace('_', '-').toLowerCase();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,14 @@ public String toSafeString(List<WildcardMatcher> value) {
.dynamic(true)
.buildWithDefault(TraceContinuationStrategy.CONTINUE);

private final ConfigurationOption<ActivationMethod> activationMethod = ConfigurationOption.enumOption(ActivationMethod.class)
.key("activation_method")
.configurationCategory(CORE_CATEGORY)
.tags("internal")
.description("telling the agent what activated it, used for telemetry and should not be set unless supported by ActivationMethod")
.dynamic(true)
.buildWithDefault(ActivationMethod.UNKNOWN);

public boolean isEnabled() {
return enabled.get();
}
Expand Down Expand Up @@ -1077,6 +1085,10 @@ public TraceContinuationStrategy getTraceContinuationStrategy() {
return traceContinuationStrategy.get();
}

public ActivationMethod getActivationMethod() {
return activationMethod.get();
}

public enum EventType {
/**
* Request bodies will never be reported
Expand Down Expand Up @@ -1119,4 +1131,5 @@ public String toString() {
return name().toLowerCase();
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -333,9 +333,14 @@ public ErrorCapture captureException(@Nullable Throwable e, @Nullable AbstractSp

@Nullable
private ErrorCapture captureException(long epochMicros, @Nullable Throwable e, @Nullable AbstractSpan<?> parent, @Nullable ClassLoader initiatingClassLoader) {
if (!isRunning()) {
if (!isRunning() || e == null) {
return null;
}

while (e != null && WildcardMatcher.anyMatch(coreConfiguration.getUnnestExceptions(), e.getClass().getName()) != null) {
e = e.getCause();
}

// note: if we add inheritance support for exception filtering, caching would be required for performance
if (e != null && !WildcardMatcher.isAnyMatch(coreConfiguration.getIgnoreExceptions(), e.getClass().getName())) {
ErrorCapture error = errorPool.createInstance();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,13 @@
*/
package co.elastic.apm.agent.impl.error;

import co.elastic.apm.agent.configuration.CoreConfiguration;
import co.elastic.apm.agent.impl.ElasticApmTracer;
import co.elastic.apm.agent.impl.context.TransactionContext;
import co.elastic.apm.agent.impl.stacktrace.StacktraceConfiguration;
import co.elastic.apm.agent.impl.transaction.AbstractSpan;
import co.elastic.apm.agent.impl.transaction.Span;
import co.elastic.apm.agent.impl.transaction.TraceContext;
import co.elastic.apm.agent.impl.transaction.Transaction;
import co.elastic.apm.agent.common.util.WildcardMatcher;
import co.elastic.apm.agent.objectpool.Recyclable;
import co.elastic.apm.agent.sdk.logging.Logger;
import co.elastic.apm.agent.sdk.logging.LoggerFactory;
Expand Down Expand Up @@ -153,11 +151,7 @@ public TraceContext getTraceContext() {
}

public void setException(Throwable e) {
if (WildcardMatcher.anyMatch(tracer.getConfig(CoreConfiguration.class).getUnnestExceptions(), e.getClass().getName()) != null) {
this.exception = e.getCause();
} else {
this.exception = e;
}
exception = e;
}

public StringBuilder getCulprit() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@
package co.elastic.apm.agent.impl.metadata;


import co.elastic.apm.agent.configuration.ActivationMethod;
import co.elastic.apm.agent.configuration.CoreConfiguration;
import co.elastic.apm.agent.impl.ElasticApmTracer;
import co.elastic.apm.agent.impl.GlobalTracer;
import co.elastic.apm.agent.util.PrivilegedActionUtils;

import java.lang.management.ManagementFactory;
import java.util.List;
import java.util.UUID;

/**
Expand All @@ -43,14 +51,22 @@ public class Agent {
*/
private final String ephemeralId;

/**
* The way the agent was activated, e.g."javaagent-flag"
* per <a href="https://github.com/elastic/apm/blob/main/specs/agents/metadata.md#activation-method">the activation method spec</a>
* (Required)
*/
private final String activationMethod;

public Agent(String name, String version) {
this(name, version, UUID.randomUUID().toString());
this(name, version, UUID.randomUUID().toString(), null);
}

public Agent(String name, String version, String ephemeralId) {
public Agent(String name, String version, String ephemeralId, CoreConfiguration coreConfiguration) {
this.name = name;
this.version = version;
this.ephemeralId = ephemeralId;
this.activationMethod = getActivationMethod(coreConfiguration);
}

/**
Expand All @@ -75,4 +91,60 @@ public String getVersion() {
public String getEphemeralId() {
return ephemeralId;
}

/**
* Activation method of the Elastic APM agent, e.g."javaagent-flag"
* (Required)
*/
public String getActivationMethod() {
return activationMethod;
}

private String getActivationMethod(CoreConfiguration coreConfiguration) {
ActivationMethod activation = ActivationMethod.UNKNOWN;
if (coreConfiguration != null) {
activation = coreConfiguration.getActivationMethod();
if (activation.equals(ActivationMethod.UNKNOWN)) {
//Need to infer it
String elasticJavaagentOnTheCommandline = getElasticJavaagentOnTheCommandline();
String javaToolOptions = PrivilegedActionUtils.getEnv("JAVA_TOOL_OPTIONS");
if (javaToolOptions != null && elasticJavaagentOnTheCommandline != null && javaToolOptions.contains(elasticJavaagentOnTheCommandline)) {
activation = ActivationMethod.ENV_ATTACH;
} else if(elasticJavaagentOnTheCommandline != null) {
activation = ActivationMethod.JAVAAGENT_FLAG;
}
}
}
return activation.toReferenceString();
}

private static String getAgentJarFilename() {
String agentLocation = PrivilegedActionUtils.getProtectionDomain(GlobalTracer.class).getCodeSource().getLocation().getFile();
if (agentLocation != null) {
String agentJarFile = agentLocation.replace('\\', '/');
if (agentJarFile.contains("/")) {
return agentJarFile.substring(agentLocation.lastIndexOf('/') + 1, agentLocation.length());
} else {
return agentJarFile;
}
} else {
return null;
}
}

private static String getElasticJavaagentOnTheCommandline() {
String agentJarFile = getAgentJarFilename();
if (agentJarFile != null) {
List<String> javaArgs = ManagementFactory.getRuntimeMXBean().getInputArguments();
if (javaArgs != null) {
//if there is more than one, this will return the first, which is the correct algorithm
for (String javaArg : javaArgs) {
if (javaArg.startsWith("-javaagent:") && javaArg.contains(agentJarFile)) {
return javaArg;
}
}
}
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public Service createService(CoreConfiguration coreConfiguration, String ephemer
.withName(coreConfiguration.getServiceName())
.withVersion(coreConfiguration.getServiceVersion())
.withEnvironment(coreConfiguration.getEnvironment())
.withAgent(new Agent("java", VersionUtils.getAgentVersion(), ephemeralId))
.withAgent(new Agent("java", VersionUtils.getAgentVersion(), ephemeralId, coreConfiguration))
.withRuntime(new RuntimeInfo("Java", System.getProperty("java.version")))
.withLanguage(new Language("Java", System.getProperty("java.version")))
.withNode(new Node(coreConfiguration.getServiceNodeName()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,7 @@ private static void serializeService(@Nullable String name, @Nullable String ver
private static void serializeAgent(final Agent agent, final StringBuilder replaceBuilder, final JsonWriter jw) {
writeFieldName("agent", jw);
jw.writeByte(JsonWriter.OBJECT_START);
writeField("activation_method", agent.getActivationMethod(), replaceBuilder, jw);
writeField("name", agent.getName(), replaceBuilder, jw);
writeField("ephemeral_id", agent.getEphemeralId(), replaceBuilder, jw);
writeLastField("version", agent.getVersion(), replaceBuilder, jw);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* 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.agent.configuration;

public class ActivationTestExampleApp {

public static void main(String[] args) throws InterruptedException {
// Just sleep for 5 minutes then exit
Thread.sleep(5*60*1000);
}
}
Loading

0 comments on commit 101cd83

Please sign in to comment.