Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions CHANGELOG.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,39 @@ metricset tag keys - {pull}1413[1413]
* Fix service name error when updating a web app on a Servlet container - {pull}1326[#1326]
* Fix remote attach 'jps' executable not found when 'java' binary is symlinked ot a JRE - {pull}1352[#1352]

[[release-notes-1.18.0]]
==== 1.18.0 - 2020/09/08

[float]
===== Features
* Deprecating `ignore_urls` config in favour of <<config-transaction-ignore-urls, `transaction_ignore_urls`>> to align
with other agents, while still allowing the old config name for backward compatibility - {pull}1315[#1315]
* Enabling instrumentation of classes compiled with Java 1.4. This is reverting the restriction of instrumenting only
bytecode of Java 1.5 or higher ({pull}320[#320]), which was added due to potential `VerifyError`. Such errors should be
avoided now by the usage of `TypeConstantAdjustment` - {pull}1317[#1317]
* Enabling agent to work without attempting any communication with APM server, by allowing setting `server_urls` with
an empty string - {pull}1295[#1295]
* Add <<metrics-micrometer, micrometer support>> - {pull}1303[#1303]
* Add `profiling_inferred_spans_lib_directory` option to override the default temp directory used for exporting the async-profiler library.
This is useful for server-hardened environments where `/tmp` is often configured with `noexec`, leading to `java.lang.UnsatisfiedLinkError` errors - {pull}1350[#1350]
* Create spans for Servlet dispatches to FORWARD, INCLUDE and ERROR - {pull}1212[#1212]
* Support JDK 11 HTTPClient - {pull}1307[#1307]
* Lazily create profiler temporary files {pull}1360[#1360]
* Convert the followings to Indy Plugins (see details in <<release-notes-1.18.0.rc1, 1.18.0-rc1 relase notes>>): gRPC,
AsyncHttpClient, Apache HttpClient
* The agent now collects cgroup memory metrics (see details in <<metrics-cgroup,Metrics page>>)
* Update async-profiler to 1.8.1 {pull}1382[#1382]

[float]
===== Bug fixes
* Fixes a `NoClassDefFoundError` in the JMS instrumentation of `MessageListener` - {pull}1287[#1287]
* Fix `/ by zero` error message when setting `server_urls` with an empty string - {pull}1295[#1295]
* Fix `ClassNotFoundException` or `ClassCastException` in some cases where special log4j configurations are used - {pull}1322[#1322]
* Fix `NumberFormatException` when using early access Java version - {pull}1325[#1325]
* Fix `service_name` config being ignored when set to the same auto-discovered default value - {pull}1324[#1324]
* Fix service name error when updating a web app on a Servlet container - {pull}1326[#1326]
* Fix remote attach 'jps' executable not found when 'java' binary is symlinked ot a JRE - {pull}1352[#1352]

[[release-notes-1.18.0.rc1]]
==== 1.18.0.RC1 - 2020/07/22

Expand Down
46 changes: 45 additions & 1 deletion apm-agent-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,12 @@
<version>1.2</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.7</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
Expand Down Expand Up @@ -198,10 +204,48 @@
<artifactId>testcontainers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20180130</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createSourcesJar>true</createSourcesJar>
<shadeSourcesContent>true</shadeSourcesContent>
<shadedArtifactAttached>false</shadedArtifactAttached>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>**/module-info.class</exclude>
<exclude>META-INF/versions/**</exclude>
</excludes>
</filter>
</filters>
<relocations>
<relocation>
<pattern>org.apache.commons.codec</pattern>
<shadedPattern>co.elastic.apm.agent.shaded.codec</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>

<plugin>
<artifactId>maven-jar-plugin</artifactId>
<executions>
Expand All @@ -215,4 +259,4 @@
</plugins>
</build>

</project>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* 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.bci;

public class SFConfigInfo {
private String key;

private String serviceName;

private SFTagInfo tags;

public String getKey() {
return this.key;
}

public void setKey(String key) {
this.key = key;
}

public String getServiceName() {
return this.serviceName;
}

public void setServiceName(String serviceName) {
this.serviceName = serviceName;
}

public SFTagInfo getTags() {
return this.tags;
}

public void setTags(SFTagInfo tags) {
this.tags = tags;
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* 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.bci;

public class SFTagInfo {
private String Name;

private String appName;

private String projectName;

public String getName() {
return this.Name;
}

public void setName(String name) {
this.Name = name;
}

public String getAppName() {
return this.appName;
}

public void setAppName(String appName) {
this.appName = appName;
}

public String getProjectName() {
return this.projectName;
}

public void setProjectName(String projectName) {
this.projectName = projectName;
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
/*
* 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.bci.bytebuddy;

import co.elastic.apm.agent.util.ExecutorUtils;
import com.blogspot.mydailyjava.weaklockfree.WeakConcurrentMap;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.pool.TypePool;

import javax.annotation.Nullable;
import java.lang.ref.SoftReference;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

/**
* Caches {@link TypeDescription}s which speeds up type matching -
* especially when the matching requires lookup of other {@link TypeDescription}s.
* Such as when in order to match a type, it's superclass has to be determined.
* Without a type pool cache those types would have to be re-loaded from the file system if their {@link Class} has not been loaded yet.
* <p>
* In order to avoid {@link OutOfMemoryError}s because of this cache,
* the {@link TypePool.CacheProvider}s are wrapped in a {@link SoftReference}
* </p>
*/
public class SoftlyReferencingTypePoolCache extends AgentBuilder.PoolStrategy.WithTypePoolCache {

/*
* Weakly referencing ClassLoaders to avoid class loader leaks
* Softly referencing the type pool cache so that it does not cause OOMEs
* deliberately doesn't use WeakMapSupplier as this class manages the cleanup manually
*/
private final WeakConcurrentMap<ClassLoader, CacheProviderWrapper> cacheProviders =
new WeakConcurrentMap<ClassLoader, CacheProviderWrapper>(false);
private final ElementMatcher<ClassLoader> ignoredClassLoaders;

public SoftlyReferencingTypePoolCache(final TypePool.Default.ReaderMode readerMode,
final int clearIfNotAccessedSinceMinutes, ElementMatcher.Junction<ClassLoader> ignoredClassLoaders) {
super(readerMode);
ExecutorUtils.createSingleThreadSchedulingDaemonPool("type-cache-pool-cleaner")
.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
clearIfNotAccessedSince(clearIfNotAccessedSinceMinutes);
cacheProviders.expungeStaleEntries();
}
}, 1, 1, TimeUnit.MINUTES);
this.ignoredClassLoaders = ignoredClassLoaders;
}

@Override
protected TypePool.CacheProvider locate(ClassLoader classLoader) {
if (ignoredClassLoaders.matches(classLoader)) {
return TypePool.CacheProvider.Simple.withObjectType();
}
classLoader = classLoader == null ? getBootstrapMarkerLoader() : classLoader;
CacheProviderWrapper cacheProviderRef = cacheProviders.get(classLoader);
if (cacheProviderRef == null || cacheProviderRef.get() == null) {
cacheProviderRef = new CacheProviderWrapper();
cacheProviders.put(classLoader, cacheProviderRef);
// accommodate for race condition
cacheProviderRef = cacheProviders.get(classLoader);
}
final TypePool.CacheProvider cacheProvider = cacheProviderRef.get();
// guard against edge case when the soft reference has already been cleared since evaluating the loop condition
return cacheProvider != null ? cacheProvider : TypePool.CacheProvider.Simple.withObjectType();
}

/**
* Clears the type pool cache if it has not been accessed for the specified amount of time.
* <p>
* This cache is mostly useful while the application starts and warms up.
* After a certain point, all classes are loaded and this cache is not needed anymore
* </p>
* <p>
* Evicting the whole cache at once has advantages over evicting on an entry-based level:
* A resolution never gets stale or outdated, which is the main use case for having a max age for an entry.
* Also, this model only works when the cache is frequently accessed,
* as most caches only evict stale entries when interacting with the cache.
* In our scenario,
* the cache is not accessed at all once all classes have been loaded which means it would never get cleared.
* </p>
* <p>
* Two exceptions of that norm are (re-)deploying a web application at runtime and dynamically loading of classes,
* which cause interactions after the initial startup.
* </p>
*
* @param clearIfNotAccessedSinceMinutes the time in minutes after which the cache should be cleared
*/
void clearIfNotAccessedSince(long clearIfNotAccessedSinceMinutes) {
for (Map.Entry<ClassLoader, CacheProviderWrapper> entry : cacheProviders) {
if (System.currentTimeMillis() >= entry.getValue().getLastAccess() + TimeUnit.MINUTES.toMillis(clearIfNotAccessedSinceMinutes)) {
cacheProviders.remove(entry.getKey());
}
}
}

WeakConcurrentMap<ClassLoader, CacheProviderWrapper> getCacheProviders() {
return cacheProviders;
}

private static class CacheProviderWrapper {
private final AtomicLong lastAccess = new AtomicLong(System.currentTimeMillis());
private final SoftReference<TypePool.CacheProvider> delegate;

private CacheProviderWrapper() {
this.delegate = new SoftReference<TypePool.CacheProvider>(new TypePool.CacheProvider.Simple());
}

long getLastAccess() {
return lastAccess.get();
}

@Nullable
TypePool.CacheProvider get() {
return delegate.get();
}
}

/**
* Copied from {@link Simple#getBootstrapMarkerLoader()}
* <p>
* Returns the class loader to serve as a cache key if a cache provider for the bootstrap class loader is requested.
* This class loader is represented by {@code null} in the JVM which is an invalid value for many {@link ConcurrentMap}
* implementations.
* </p>
* <p>
* By default, {@link ClassLoader#getSystemClassLoader()} is used as such a key as any resource location for the
* bootstrap class loader is performed via the system class loader within Byte Buddy as {@code null} cannot be queried
* for resources via method calls such that this does not make a difference.
* </p>
*
* @return A class loader to represent the bootstrap class loader.
*/
private ClassLoader getBootstrapMarkerLoader() {
return ClassLoader.getSystemClassLoader();
}
}
Loading