From a4b013dd6be44e6d5e8f3b95ca68224f1a6dbce6 Mon Sep 17 00:00:00 2001
From: vivek-shrikhande <65864138+vivek-shrikhande@users.noreply.github.com>
Date: Mon, 7 Jun 2021 14:24:35 +0530
Subject: [PATCH 1/7] feat: Set normalized urls as Transaction names
---
CHANGELOG.asciidoc | 58 ++--
CONTRIBUTING.md | 8 +-
Jenkinsfile | 2 +-
apm-agent-api/pom.xml | 2 +-
apm-agent-attach/pom.xml | 2 +-
.../co/elastic/apm/attach/JvmDiscoverer.java | 62 +++-
.../co/elastic/apm/attach/RemoteAttacher.java | 3 +-
.../elastic/apm/attach/JvmDiscovererTest.java | 76 ++++-
apm-agent-benchmarks/pom.xml | 7 +-
.../agent/benchmark/ProfilerBenchmark.java | 2 -
apm-agent-core/pom.xml | 56 +++-
.../co/elastic/apm/agent/bci/AgentMain.java | 307 ++++++++---------
.../apm/agent/bci/ElasticApmAgent.java | 20 +-
.../apm/agent/bci/HelperClassManager.java | 70 +---
.../elastic/apm/agent/bci/IndyBootstrap.java | 4 +-
.../bci/IndyPluginClassLoaderFactory.java | 109 +++++++
.../co/elastic/apm/agent/bci/SFAgentUtil.java | 197 +++++++++++
.../elastic/apm/agent/bci/SFConfigInfo.java | 58 ++++
.../co/elastic/apm/agent/bci/SFTagInfo.java | 58 ++++
.../agent/bci/TracerAwareInstrumentation.java | 52 +--
.../apm/agent/bci/VisibleForAdvice.java | 7 +-
.../bci/bytebuddy/ErrorLoggingListener.java | 6 +-
.../MinimumClassFileVersionValidator.java | 26 +-
.../SoftlyReferencingTypePoolCache.java | 2 +-
.../classloading/IndyPluginClassLoader.java | 6 +-
.../TraceMethodInstrumentation.java | 5 +
.../apm/agent/collections/WeakMapCleaner.java | 2 +-
.../ApmServerConfigurationSource.java | 2 +-
...cutorServiceShutdownLifecycleListener.java | 42 +++
.../apm/agent/impl/ElasticApmTracer.java | 15 +-
.../agent/impl/ElasticApmTracerBuilder.java | 2 +-
.../elastic/apm/agent/impl/GlobalTracer.java | 25 +-
.../impl/circuitbreaker/CircuitBreaker.java | 2 +-
.../apm/agent/impl/context/Destination.java | 2 +-
.../impl/context/web/WebConfiguration.java | 9 +-
.../agent/logging/LoggingConfiguration.java | 34 +-
.../apm/agent/metrics/MetricRegistry.java | 24 +-
.../elastic/apm/agent/metrics/MetricSet.java | 19 +-
.../agent/metrics/builtin/CGroupMetrics.java | 308 ++++++++++++++++++
.../report/AbstractIntakeApiHandler.java | 59 +++-
.../apm/agent/report/ApmServerClient.java | 30 +-
.../agent/report/ApmServerHealthChecker.java | 4 +-
.../apm/agent/report/ApmServerReporter.java | 41 +--
.../report/IntakeV2ReportingEventHandler.java | 38 ++-
.../co/elastic/apm/agent/report/Reporter.java | 7 +-
.../agent/report/ReporterConfiguration.java | 17 +-
.../apm/agent/report/ReportingEvent.java | 50 ++-
.../report/serialize/DslJsonSerializer.java | 21 +-
.../serialize/MetricRegistryReporter.java | 71 ++++
.../serialize/MetricRegistrySerializer.java | 19 +-
.../report/serialize/PayloadSerializer.java | 5 +-
.../report/ssl/TLSFallbackSSLSocket.java | 35 +-
.../DependencyInjectingServiceLoader.java | 13 +-
.../elastic/apm/agent/util/ExecutorUtils.java | 81 +++--
.../co/elastic/apm/agent/util/IOUtils.java | 5 +-
.../co/elastic/apm/agent/util/Version.java | 3 +
...lastic.apm.agent.context.LifecycleListener | 2 +
.../agent/AbstractInstrumentationTest.java | 40 +--
.../co/elastic/apm/agent/MockReporter.java | 127 ++++----
.../java/co/elastic/apm/agent/MockTracer.java | 71 ++--
.../elastic/apm/agent/bci/AgentMainTest.java | 3 +-
.../apm/agent/bci/InstrumentationTest.java | 110 ++++++-
.../TraceMethodInstrumentationTest.java | 2 +-
.../AdviceInSubpackageInstrumentation.java | 5 -
.../agent/configuration/SpyConfiguration.java | 6 +-
.../apm/agent/impl/ElasticApmTracerTest.java | 40 +++
.../apm/agent/impl/SpanTypeBreakdownTest.java | 26 +-
.../agent/logging/JulBridgeLoggerTest.java | 9 +-
.../agent/matcher/WildcardMatcherTest.java | 284 +++-------------
.../apm/agent/metrics/MetricRegistryTest.java | 108 +++++-
.../metrics/builtin/CGroupMetricsTest.java | 132 ++++++++
.../objectpool/TestObjectPoolFactory.java | 16 +
.../objectpool/impl/BookkeeperObjectPool.java | 5 +
.../ApmServerClientProxySupportTest.java | 268 +++++++++++++++
.../apm/agent/report/ApmServerClientTest.java | 80 ++++-
.../IntakeV2ReportingEventHandlerTest.java | 17 +-
.../serialize/MetricSetSerializationTest.java | 6 +-
.../elastic/apm/agent/util/IOUtilsTest.java | 22 +-
.../src/test/java/specs/TestJsonSpec.java | 47 +++
.../json-specs/sql_signature_examples.json | 146 +++++++++
.../json-specs/sql_token_examples.json | 3 +-
.../json-specs/wildcard_matcher_tests.json | 194 +++++++++++
apm-agent-core/src/test/resources/proc/cgroup | 2 +
.../src/test/resources/proc/cgroup2 | 2 +
.../src/test/resources/proc/cgroup2_only_0 | 1 +
.../test/resources/proc/cgroup2_only_memory | 1 +
.../proc/limited/memory/memory.limit_in_bytes | 1 +
.../resources/proc/limited/memory/memory.stat | 40 +++
.../proc/limited/memory/memory.usage_in_bytes | 1 +
.../proc/sys_cgroup2/slice/memory.current | 1 +
.../proc/sys_cgroup2/slice/memory.max | 1 +
.../proc/sys_cgroup2/slice/memory.stat | 40 +++
.../slice/memory.current | 1 +
.../sys_cgroup2_unlimited/slice/memory.max | 1 +
.../sys_cgroup2_unlimited/slice/memory.stat | 40 +++
.../slice/memory.current | 1 +
.../slice/memory.max | 1 +
.../slice/memory.stat | 40 +++
.../unlimited/memory/memory.limit_in_bytes | 1 +
.../proc/unlimited/memory/memory.stat | 40 +++
.../unlimited/memory/memory.usage_in_bytes | 1 +
.../src/test/resources/specs/api_key.feature | 8 +-
.../resources/squid/squid_basic-auth.conf | 41 +++
.../test/resources/squid/squid_no-auth.conf | 38 +++
.../src/test/resources/squid/squid_passwd | 2 +
....properties => test.elasticapm.properties} | 0
...st.elasticapm.with-service-name.properties | 1 +
apm-agent-plugin-sdk/pom.xml | 2 +-
.../agent/sdk/ElasticApmInstrumentation.java | 51 +++
.../agent/sdk/state/GlobalThreadLocal.java | 8 +
.../agent/sdk/weakmap/WeakMapSupplier.java | 10 +
.../apm-apache-httpclient-plugin/pom.xml | 2 +-
.../ApacheHttpAsyncClientInstrumentation.java | 37 +--
...ttpAsyncClientRedirectInstrumentation.java | 23 +-
.../ApacheHttpClientInstrumentation.java | 58 ++--
.../BaseApacheHttpClientInstrumentation.java | 31 +-
...LegacyApacheHttpClientInstrumentation.java | 72 ++--
.../helper/ApacheHttpAsyncClientHelper.java | 58 +++-
.../ApacheHttpAsyncClientHelperImpl.java | 88 -----
.../helper/FutureCallbackWrapper.java | 4 +-
.../HttpAsyncRequestProducerWrapper.java | 4 +-
.../helper/RequestHeaderAccessor.java | 3 +
...cyApacheHttpClientInstrumentationTest.java | 6 +-
apm-agent-plugins/apm-api-plugin/pom.xml | 2 +-
.../agent/plugin/api/ApiInstrumentation.java | 5 +
.../api/CaptureSpanInstrumentation.java | 4 +
.../CaptureTransactionInstrumentation.java | 4 +
.../plugin/api/TracedInstrumentation.java | 4 +
.../plugin/api/SpanInstrumentationTest.java | 15 +-
.../co/elastic/apm/api/AnnotationApiTest.java | 1 -
.../BlockingQueueContextPropagationTest.java | 10 +-
.../api/ElasticApmApiInstrumentationTest.java | 20 +-
.../apm-asynchttpclient-plugin/pom.xml | 2 +-
...bstractAsyncHttpClientInstrumentation.java | 159 ++++-----
.../{helper => }/RequestHeaderSetter.java | 9 +-
.../apm-bootdelegation-plugin/pom.xml | 2 +-
...pDelegationClassLoaderInstrumentation.java | 5 +
apm-agent-plugins/apm-dubbo-plugin/pom.xml | 2 +-
.../dubbo/AbstractDubboInstrumentation.java | 5 +
.../AbstractDubboInstrumentationTest.java | 160 ---------
.../AlibabaDubboInstrumentationTest.java | 150 ---------
.../dubbo/ApacheDubboInstrumentationTest.java | 228 -------------
.../dubbo/api/impl/DubboTestApiImpl.java | 156 ---------
.../apm-error-logging-plugin/pom.xml | 2 +-
...ctLoggerErrorCapturingInstrumentation.java | 5 +
...stractErrorLoggingInstrumentationTest.java | 12 +-
.../apm-es-restclient-plugin-5_6/pom.xml | 2 +-
.../apm-es-restclient-plugin-6_4/pom.xml | 2 +-
.../apm-es-restclient-plugin-7_1/pom.xml | 2 +-
.../apm-es-restclient-plugin-common/pom.xml | 2 +-
...lasticsearchRestClientInstrumentation.java | 5 +
.../AbstractEsClientInstrumentationTest.java | 14 +-
.../apm-es-restclient-plugin/pom.xml | 2 +-
apm-agent-plugins/apm-grails-plugin/pom.xml | 2 +-
.../GrailsTransactionNameInstrumentation.java | 5 +
.../apm-grpc/apm-grpc-plugin/pom.xml | 2 +-
.../apm/agent/grpc/BaseInstrumentation.java | 23 +-
.../agent/grpc/ChannelInstrumentation.java | 40 +--
.../grpc/ClientCallImplInstrumentation.java | 125 ++-----
.../GrpcHelperImpl.java => GrpcHelper.java} | 134 ++++++--
.../ServerCallHandlerInstrumentation.java | 47 +--
.../agent/grpc/ServerCallInstrumentation.java | 23 +-
.../ServerCallListenerInstrumentation.java | 95 ++----
.../apm/agent/grpc/helper/GrpcHelper.java | 181 ----------
...AbstractGrpcClientInstrumentationTest.java | 16 +-
.../grpc/AbstractGrpcContextHeadersTest.java | 8 +-
...AbstractGrpcServerInstrumentationTest.java | 11 +-
.../grpc/testapp/AbstractGrpcAppTest.java | 3 +-
.../apm-grpc/apm-grpc-test-1.6.1/pom.xml | 2 +-
apm-agent-plugins/apm-grpc/pom.xml | 2 +-
.../apm-hibernate-search-plugin-5_x/pom.xml | 2 +-
.../v5_x/HibernateSearch5Instrumentation.java | 5 +
.../apm-hibernate-search-plugin-6_x/pom.xml | 2 +-
.../v6_x/HibernateSearch6Instrumentation.java | 5 +
.../pom.xml | 2 +-
.../apm-hibernate-search-plugin/pom.xml | 2 +-
apm-agent-plugins/apm-httpclient-core/pom.xml | 2 +-
.../http/client/HttpClientHelperTest.java | 1 -
...AbstractHttpClientInstrumentationTest.java | 30 +-
.../apm-java-concurrent-plugin/pom.xml | 2 +-
.../concurrent/ExecutorInstrumentation.java | 6 +-
.../ForkJoinTaskInstrumentation.java | 5 -
.../apm/agent/concurrent/JavaConcurrent.java | 4 +-
...leCallableForkJoinTaskInstrumentation.java | 5 -
.../AsyncTraceMethodInstrumentationTest.java | 2 +-
.../agent/concurrent/ScopeManagementTest.java | 22 +-
apm-agent-plugins/apm-jaxrs-plugin/pom.xml | 2 +-
.../JaxRsTransactionNameInstrumentation.java | 5 +
...xRsTransactionNameInstrumentationTest.java | 32 +-
apm-agent-plugins/apm-jaxws-plugin/pom.xml | 2 +-
.../JaxWsTransactionNameInstrumentation.java | 5 +
apm-agent-plugins/apm-jdbc-plugin/pom.xml | 2 +-
.../apm/agent/jdbc/JdbcInstrumentation.java | 5 -
.../apm/agent/jdbc/signature/ScannerTest.java | 54 ++-
.../jdbc/signature/SignatureParserTest.java | 64 +++-
.../src/test/resources/signature_tests.json | 144 --------
.../apm-jdk-httpclient-plugin/pom.xml | 42 +++
.../AbstractHttpClientInstrumentation.java | 59 ++++
.../HttpClientAsyncInstrumentation.java | 92 ++++++
.../httpclient/HttpClientInstrumentation.java | 83 +++++
.../HttpClientRequestPropertyAccessor.java | 45 +++
.../HttpRequestHeadersInstrumentation.java | 75 +++++
...ic.apm.agent.sdk.ElasticApmInstrumentation | 3 +
.../HttpClientAsyncInstrumentationTest.java | 54 +++
.../HttpClientInstrumentationTest.java | 63 ++++
apm-agent-plugins/apm-jms-plugin/pom.xml | 2 +-
.../apm/agent/jms/BaseJmsInstrumentation.java | 5 +
.../apm/agent/jms/JmsInstrumentationIT.java | 2 -
.../apm/agent/jms/spring/SpringJmsTest.java | 1 -
apm-agent-plugins/apm-jmx-plugin/pom.xml | 2 +-
.../apm/agent/jmx/JmxMetricTrackerTest.java | 9 +-
apm-agent-plugins/apm-jsf-plugin/pom.xml | 2 +-
.../jsf/JsfLifecycleInstrumentation.java | 5 +
.../apm-kafka-base-plugin/pom.xml | 2 +-
.../agent/kafka/BaseKafkaInstrumentation.java | 5 +
.../apm/agent/kafka/KafkaLegacyClientIT.java | 1 -
.../apm-kafka-headers-plugin/pom.xml | 2 +-
.../co/elastic/apm/agent/kafka/KafkaIT.java | 1 -
.../apm/agent/kafka/KafkaLegacyBrokerIT.java | 1 -
apm-agent-plugins/apm-kafka-plugin/pom.xml | 2 +-
.../apm-log-correlation-plugin/pom.xml | 2 +-
.../agent/mdc/MdcActivationListenerTest.java | 4 +
.../apm-log-shipper-plugin/pom.xml | 2 +-
.../log/shipper/ApmServerLogShipper.java | 6 +-
.../log/shipper/ApmServerLogShipperTest.java | 42 ++-
.../apm-micrometer-plugin/pom.xml | 30 ++
.../micrometer/MicrometerInstrumentation.java | 85 +++++
.../MicrometerMeterRegistrySerializer.java | 199 +++++++++++
.../micrometer/MicrometerMetricsReporter.java | 111 +++++++
.../apm/agent/micrometer}/package-info.java | 9 +-
...ic.apm.agent.sdk.ElasticApmInstrumentation | 1 +
.../MicrometerInstrumentationTest.java | 65 ++++
.../MicrometerMetricsReporterTest.java | 238 ++++++++++++++
.../apm-mongoclient-plugin/pom.xml | 2 +-
.../MongoClientInstrumentation.java | 5 +
...bstractMongoClientInstrumentationTest.java | 18 +-
apm-agent-plugins/apm-okhttp-plugin/pom.xml | 2 +-
.../AbstractOkHttp3ClientInstrumentation.java | 5 +
.../AbstractOkHttpClientInstrumentation.java | 5 +
.../apm-opentracing-plugin/pom.xml | 2 +-
.../OpenTracingBridgeInstrumentation.java | 5 +
apm-agent-plugins/apm-process-plugin/pom.xml | 2 +-
.../process/BaseProcessInstrumentation.java | 5 -
.../CommonsExecAsyncInstrumentation.java | 5 -
.../apm/agent/process/ProcessHelperTest.java | 1 +
.../apm-profiling-plugin/pom.xml | 2 +-
.../profiler/ProfilingConfiguration.java | 33 +-
.../apm/agent/profiler/ProfilingFactory.java | 23 +-
.../apm/agent/profiler/SamplingProfiler.java | 115 +++++--
.../profiler/asyncprofiler/AsyncProfiler.java | 23 +-
.../profiler/collections/IntIntConsumer.java | 4 +-
.../collections/LongLongConsumer.java | 4 +-
.../libasyncProfiler-linux-arm.so | Bin 220552 -> 236152 bytes
.../libasyncProfiler-linux-x64.so | Bin 259553 -> 271714 bytes
.../libasyncProfiler-linux-x86.so | Bin 253580 -> 253732 bytes
.../asyncprofiler/libasyncProfiler-macos.so | Bin 193512 -> 198640 bytes
.../profiler/SamplingProfilerQueueTest.java | 8 +-
.../profiler/SamplingProfilerReplay.java | 2 -
.../agent/profiler/SamplingProfilerTest.java | 168 ++++++++--
.../asyncprofiler/AsyncProfilerTest.java | 64 ++++
.../apm-quartz-job-plugin/pom.xml | 2 +-
.../JobTransactionNameInstrumentation.java | 5 +
.../apm-jedis-2-tests/pom.xml | 2 +-
.../jedis/Jedis2InstrumentationTest.java | 13 +-
.../apm-jedis-3-tests/pom.xml | 2 +-
.../apm-redis-plugin/apm-jedis-plugin/pom.xml | 2 +-
.../redis/jedis/JedisInstrumentation.java | 5 +
.../jedis/JedisSpanNameInstrumentation.java | 5 +
.../jedis/Jedis1InstrumentationTest.java | 11 +-
.../apm-lettuce-3-tests/pom.xml | 2 +-
.../lettuce/Lettuce3InstrumentationTest.java | 11 +-
.../apm-lettuce-plugin/pom.xml | 2 +-
.../lettuce/Lettuce34Instrumentation.java | 5 +
.../Lettuce5StartSpanInstrumentation.java | 4 +
.../Lettuce5StopSpanInstrumentation.java | 5 +
.../lettuce/Lettuce4InstrumentationTest.java | 25 +-
.../lettuce/Lettuce5InstrumentationTest.java | 42 +--
.../apm-redis-plugin/apm-redis-common/pom.xml | 2 +-
.../AbstractRedisInstrumentationTest.java | 2 +
.../apm-redisson-plugin/pom.xml | 2 +-
.../RedisConnectionInstrumentation.java | 5 +
.../redisson/RedissonInstrumentationTest.java | 94 +++---
apm-agent-plugins/apm-redis-plugin/pom.xml | 2 +-
.../apm-scala-concurrent-plugin/pom.xml | 2 +-
.../concurrent/FutureInstrumentation.java | 5 +
.../apm-scheduled-annotation-plugin/pom.xml | 2 +-
...heduledTransactionNameInstrumentation.java | 5 +
.../scheduled/TimerTaskInstrumentation.java | 5 +
...ledTransactionNameInstrumentationTest.java | 16 +-
.../TimerTaskInstrumentationTest.java | 95 +++---
apm-agent-plugins/apm-servlet-plugin/pom.xml | 2 +-
.../AbstractServletInstrumentation.java | 5 -
.../servlet/RequestDispatcherSpanType.java} | 31 +-
.../apm/agent/servlet/ServletApiAdvice.java | 151 ++++++---
.../apm/agent/servlet/ServletGlobalState.java | 4 +-
.../servlet/ServletTransactionHelper.java | 4 +-
.../servlet/ServletInstrumentationTest.java | 131 +++++++-
.../servlet/ServletTransactionHelperTest.java | 27 +-
.../servlet/TestRequestBodyCapturing.java | 10 -
.../apm-spring-resttemplate-plugin/pom.xml | 2 +-
.../SpringRestTemplateInstrumentation.java | 5 +
.../apm-spring-webmvc-plugin/pom.xml | 2 +-
.../ExceptionHandlerInstrumentation.java | 5 +
.../SpringServiceNameInstrumentation.java | 5 +
.../SpringTransactionNameInstrumentation.java | 42 ++-
.../webmvc/ViewRenderInstrumentation.java | 5 +
...ctExceptionHandlerInstrumentationTest.java | 2 +-
...tractViewRenderingInstrumentationTest.java | 2 +-
.../apm-urlconnection-plugin/pom.xml | 2 +-
.../HttpUrlConnectionInstrumentation.java | 5 -
.../SSLContextInstrumentation.java | 5 -
.../SSLContextInstrumentationTest.java | 2 +-
apm-agent-plugins/pom.xml | 4 +-
apm-opentracing/pom.xml | 2 +-
.../OpenTracingBridgeCloseTest.java | 8 +-
.../opentracing/OpenTracingBridgeTest.java | 8 +-
cloudfoundry/index.yml | 1 +
docs/configuration.asciidoc | 80 ++++-
docs/metrics.asciidoc | 227 ++++++++++++-
docs/setup-ssl.asciidoc | 34 ++
docs/setup.asciidoc | 10 +-
docs/supported-technologies.asciidoc | 16 +-
docs/troubleshooting.asciidoc | 33 +-
elastic-apm-agent/pom.xml | 13 +-
.../configuration/ConfigurationExporter.java | 30 +-
.../ConfigurationExporterTest.java | 14 +
.../pom.xml | 2 +-
.../apm/servlet/tests/ServletApiTestApp.java | 23 ++
.../cdi-app/cdi-app-dependent/pom.xml | 2 +-
.../cdi-app/cdi-app-standalone/pom.xml | 2 +-
integration-tests/cdi-app/pom.xml | 2 +-
.../external-plugin-test/pom.xml | 6 +-
.../jsf-app/jsf-app-dependent/pom.xml | 2 +-
.../jsf-app/jsf-app-standalone/pom.xml | 2 +-
integration-tests/jsf-app/pom.xml | 2 +-
integration-tests/pom.xml | 2 +-
integration-tests/simple-webapp/pom.xml | 2 +-
.../java/co/elastic/webapp/ErrorServlet.java | 42 +++
.../co/elastic/webapp/ForwardingServlet.java | 30 +-
.../co/elastic/webapp/IncludingServlet.java | 17 +-
.../src/main/webapp/WEB-INF/web.xml | 21 ++
integration-tests/soap-test/pom.xml | 2 +-
integration-tests/spring-boot-1-5/pom.xml | 2 +-
.../apm/spring/boot/SpringBoot1_5IT.java | 2 +-
integration-tests/spring-boot-2/pom.xml | 2 +-
.../spring-boot-2/spring-boot-2-base/pom.xml | 2 +-
.../spring/boot/AbstractSpringBootTest.java | 5 +-
.../spring-boot-2/spring-boot-2-jetty/pom.xml | 2 +-
.../spring-boot-2-tomcat/pom.xml | 2 +-
.../spring-boot-2-undertow/pom.xml | 2 +-
mvnw | 0
mvnw.cmd | 0
pom.xml | 54 +--
scripts/jenkins/branch_creation.sh | 0
scripts/jenkins/build_docker.sh | 0
scripts/jenkins/check_maven.sh | 0
scripts/jenkins/docs_check.sh | 0
scripts/jenkins/docs_release_check/check.py | 0
scripts/jenkins/fetch_nexus_id.sh | 4 +
scripts/jenkins/generate_release_notes_url.sh | 0
scripts/jenkins/is_major.sh | 0
scripts/jenkins/maven_artifact_url.sh | 0
scripts/jenkins/push_docker.sh | 0
scripts/jenkins/release_env.sh | 0
scripts/jenkins/run-benchmarks.sh | 0
scripts/jenkins/smoketests-01.sh | 0
scripts/jenkins/smoketests-02.sh | 0
scripts/jenkins/tags.sh | 12 +
scripts/jenkins/update_cloudfoundry.sh | 0
369 files changed, 6906 insertions(+), 3350 deletions(-)
create mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyPluginClassLoaderFactory.java
create mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/bci/SFAgentUtil.java
create mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/bci/SFConfigInfo.java
create mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/bci/SFTagInfo.java
create mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/context/ExecutorServiceShutdownLifecycleListener.java
create mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/metrics/builtin/CGroupMetrics.java
create mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/report/serialize/MetricRegistryReporter.java
create mode 100644 apm-agent-core/src/test/java/co/elastic/apm/agent/metrics/builtin/CGroupMetricsTest.java
create mode 100644 apm-agent-core/src/test/java/co/elastic/apm/agent/report/ApmServerClientProxySupportTest.java
create mode 100644 apm-agent-core/src/test/java/specs/TestJsonSpec.java
create mode 100644 apm-agent-core/src/test/resources/json-specs/sql_signature_examples.json
rename apm-agent-plugins/apm-jdbc-plugin/src/test/resources/scanner_tests.json => apm-agent-core/src/test/resources/json-specs/sql_token_examples.json (99%)
create mode 100644 apm-agent-core/src/test/resources/json-specs/wildcard_matcher_tests.json
create mode 100644 apm-agent-core/src/test/resources/proc/cgroup
create mode 100644 apm-agent-core/src/test/resources/proc/cgroup2
create mode 100644 apm-agent-core/src/test/resources/proc/cgroup2_only_0
create mode 100644 apm-agent-core/src/test/resources/proc/cgroup2_only_memory
create mode 100644 apm-agent-core/src/test/resources/proc/limited/memory/memory.limit_in_bytes
create mode 100644 apm-agent-core/src/test/resources/proc/limited/memory/memory.stat
create mode 100644 apm-agent-core/src/test/resources/proc/limited/memory/memory.usage_in_bytes
create mode 100644 apm-agent-core/src/test/resources/proc/sys_cgroup2/slice/memory.current
create mode 100644 apm-agent-core/src/test/resources/proc/sys_cgroup2/slice/memory.max
create mode 100644 apm-agent-core/src/test/resources/proc/sys_cgroup2/slice/memory.stat
create mode 100644 apm-agent-core/src/test/resources/proc/sys_cgroup2_unlimited/slice/memory.current
create mode 100644 apm-agent-core/src/test/resources/proc/sys_cgroup2_unlimited/slice/memory.max
create mode 100644 apm-agent-core/src/test/resources/proc/sys_cgroup2_unlimited/slice/memory.stat
create mode 100644 apm-agent-core/src/test/resources/proc/sys_cgroup2_unlimited_stat_different_order/slice/memory.current
create mode 100644 apm-agent-core/src/test/resources/proc/sys_cgroup2_unlimited_stat_different_order/slice/memory.max
create mode 100644 apm-agent-core/src/test/resources/proc/sys_cgroup2_unlimited_stat_different_order/slice/memory.stat
create mode 100644 apm-agent-core/src/test/resources/proc/unlimited/memory/memory.limit_in_bytes
create mode 100644 apm-agent-core/src/test/resources/proc/unlimited/memory/memory.stat
create mode 100644 apm-agent-core/src/test/resources/proc/unlimited/memory/memory.usage_in_bytes
create mode 100644 apm-agent-core/src/test/resources/squid/squid_basic-auth.conf
create mode 100644 apm-agent-core/src/test/resources/squid/squid_no-auth.conf
create mode 100644 apm-agent-core/src/test/resources/squid/squid_passwd
rename apm-agent-core/src/test/resources/{elasticapm.properties => test.elasticapm.properties} (100%)
create mode 100644 apm-agent-core/src/test/resources/test.elasticapm.with-service-name.properties
delete mode 100644 apm-agent-plugins/apm-apache-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/helper/ApacheHttpAsyncClientHelperImpl.java
rename apm-agent-plugins/apm-asynchttpclient-plugin/src/main/java/co/elastic/apm/agent/asynchttpclient/{helper => }/RequestHeaderSetter.java (84%)
delete mode 100644 apm-agent-plugins/apm-dubbo-plugin/src/test/java/co/elastic/apm/agent/dubbo/AbstractDubboInstrumentationTest.java
delete mode 100644 apm-agent-plugins/apm-dubbo-plugin/src/test/java/co/elastic/apm/agent/dubbo/AlibabaDubboInstrumentationTest.java
delete mode 100644 apm-agent-plugins/apm-dubbo-plugin/src/test/java/co/elastic/apm/agent/dubbo/ApacheDubboInstrumentationTest.java
delete mode 100644 apm-agent-plugins/apm-dubbo-plugin/src/test/java/co/elastic/apm/agent/dubbo/api/impl/DubboTestApiImpl.java
rename apm-agent-plugins/apm-grpc/apm-grpc-plugin/src/main/java/co/elastic/apm/agent/grpc/{helper/GrpcHelperImpl.java => GrpcHelper.java} (68%)
delete mode 100644 apm-agent-plugins/apm-grpc/apm-grpc-plugin/src/main/java/co/elastic/apm/agent/grpc/helper/GrpcHelper.java
create mode 100644 apm-agent-plugins/apm-jdk-httpclient-plugin/pom.xml
create mode 100644 apm-agent-plugins/apm-jdk-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/AbstractHttpClientInstrumentation.java
create mode 100644 apm-agent-plugins/apm-jdk-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/HttpClientAsyncInstrumentation.java
create mode 100644 apm-agent-plugins/apm-jdk-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/HttpClientInstrumentation.java
create mode 100644 apm-agent-plugins/apm-jdk-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/HttpClientRequestPropertyAccessor.java
create mode 100644 apm-agent-plugins/apm-jdk-httpclient-plugin/src/main/java/co/elastic/apm/agent/httpclient/HttpRequestHeadersInstrumentation.java
create mode 100644 apm-agent-plugins/apm-jdk-httpclient-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation
create mode 100644 apm-agent-plugins/apm-jdk-httpclient-plugin/src/test/java/co/elastic/apm/agent/httpclient/HttpClientAsyncInstrumentationTest.java
create mode 100644 apm-agent-plugins/apm-jdk-httpclient-plugin/src/test/java/co/elastic/apm/agent/httpclient/HttpClientInstrumentationTest.java
create mode 100644 apm-agent-plugins/apm-micrometer-plugin/pom.xml
create mode 100644 apm-agent-plugins/apm-micrometer-plugin/src/main/java/co/elastic/apm/agent/micrometer/MicrometerInstrumentation.java
create mode 100644 apm-agent-plugins/apm-micrometer-plugin/src/main/java/co/elastic/apm/agent/micrometer/MicrometerMeterRegistrySerializer.java
create mode 100644 apm-agent-plugins/apm-micrometer-plugin/src/main/java/co/elastic/apm/agent/micrometer/MicrometerMetricsReporter.java
rename apm-agent-plugins/{apm-asynchttpclient-plugin/src/main/java/co/elastic/apm/agent/asynchttpclient/helper => apm-micrometer-plugin/src/main/java/co/elastic/apm/agent/micrometer}/package-info.java (88%)
create mode 100644 apm-agent-plugins/apm-micrometer-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation
create mode 100644 apm-agent-plugins/apm-micrometer-plugin/src/test/java/co/elastic/apm/agent/micrometer/MicrometerInstrumentationTest.java
create mode 100644 apm-agent-plugins/apm-micrometer-plugin/src/test/java/co/elastic/apm/agent/micrometer/MicrometerMetricsReporterTest.java
mode change 100755 => 100644 apm-agent-plugins/apm-profiling-plugin/src/main/resources/asyncprofiler/libasyncProfiler-linux-arm.so
mode change 100755 => 100644 apm-agent-plugins/apm-profiling-plugin/src/main/resources/asyncprofiler/libasyncProfiler-linux-x64.so
mode change 100755 => 100644 apm-agent-plugins/apm-profiling-plugin/src/main/resources/asyncprofiler/libasyncProfiler-linux-x86.so
mode change 100755 => 100644 apm-agent-plugins/apm-profiling-plugin/src/main/resources/asyncprofiler/libasyncProfiler-macos.so
create mode 100644 apm-agent-plugins/apm-profiling-plugin/src/test/java/co/elastic/apm/agent/profiler/asyncprofiler/AsyncProfilerTest.java
rename apm-agent-plugins/{apm-grpc/apm-grpc-plugin/src/main/java/co/elastic/apm/agent/grpc/helper/package-info.java => apm-servlet-plugin/src/main/java/co/elastic/apm/agent/servlet/RequestDispatcherSpanType.java} (58%)
create mode 100644 docs/setup-ssl.asciidoc
create mode 100644 integration-tests/simple-webapp/src/main/java/co/elastic/webapp/ErrorServlet.java
rename apm-agent-plugins/apm-dubbo-plugin/src/test/java/co/elastic/apm/agent/dubbo/api/DubboTestApi.java => integration-tests/simple-webapp/src/main/java/co/elastic/webapp/ForwardingServlet.java (65%)
rename apm-agent-plugins/apm-dubbo-plugin/src/test/java/co/elastic/apm/agent/dubbo/api/exception/BizException.java => integration-tests/simple-webapp/src/main/java/co/elastic/webapp/IncludingServlet.java (65%)
mode change 100755 => 100644 mvnw
mode change 100755 => 100644 mvnw.cmd
mode change 100755 => 100644 scripts/jenkins/branch_creation.sh
mode change 100755 => 100644 scripts/jenkins/build_docker.sh
mode change 100755 => 100644 scripts/jenkins/check_maven.sh
mode change 100755 => 100644 scripts/jenkins/docs_check.sh
mode change 100755 => 100644 scripts/jenkins/docs_release_check/check.py
create mode 100644 scripts/jenkins/fetch_nexus_id.sh
mode change 100755 => 100644 scripts/jenkins/generate_release_notes_url.sh
mode change 100755 => 100644 scripts/jenkins/is_major.sh
mode change 100755 => 100644 scripts/jenkins/maven_artifact_url.sh
mode change 100755 => 100644 scripts/jenkins/push_docker.sh
mode change 100755 => 100644 scripts/jenkins/release_env.sh
mode change 100755 => 100644 scripts/jenkins/run-benchmarks.sh
mode change 100755 => 100644 scripts/jenkins/smoketests-01.sh
mode change 100755 => 100644 scripts/jenkins/smoketests-02.sh
create mode 100644 scripts/jenkins/tags.sh
mode change 100755 => 100644 scripts/jenkins/update_cloudfoundry.sh
diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc
index e2ae7ccec9..11357f8866 100644
--- a/CHANGELOG.asciidoc
+++ b/CHANGELOG.asciidoc
@@ -20,42 +20,54 @@ endif::[]
=== Unreleased
-[[release-notes-1.18.0]]
-==== 1.18.0 - YYYY/MM/DD
+[[release-notes-1.19.0]]
+==== 1.19.0 - YYYY/MM/DD
[float]
===== Breaking changes
[float]
===== Features
-* Experimental support for runtime attachment now also for OSGi containers, JBoss, and WildFly
-* New mitigation of OSGi bootdelegation errors (`NoClassDefFoundError`).
- You can remove any `org.osgi.framework.bootdelegation` related configuration.
- This release also removes the configuration option `boot_delegation_packages`.
-* Overhaul of the `ExecutorService` instrumentation that avoids `ClassCastException` issues - {pull}1206[#1206]
-* Support for `ForkJoinPool` and `ScheduledExecutorService` (see <>)
-* Support for `ExecutorService#invokeAny` and `ExecutorService#invokeAll`
-* Added support for `java.util.TimerTask` - {pull}1235[#1235]
-* Add capturing of request body in Elasticsearch queries: `_msearch`, `_count`, `_msearch/template`, `_search/template`, `_rollup_search` - {pull}1222[#1222]
-* Add <> flag
-* Add experimental support for Scala Futures
-* The agent now collects heap memory pools metrics - {pull}1228[#1228]
[float]
===== Bug fixes
-* Fixes error capturing for log4j2 loggers. Version 1.17.0 introduced a regression.
-* Fixes `NullPointerException` related to JAX-RS and Quartz instrumentation - {pull}1249[#1249]
-* Expanding k8s pod ID discovery to some formerly non-supported environments
-* When `recording` is set to `false`, the agent will not send captured errors anymore.
-* Fixes NPE in Dubbo instrumentation that occurs when the application is acting both as a provider and as a consumer - {pull}1260[#1260]
-* Adding a delay by default what attaching the agent to Tomcat using the premain route to work around the JUL
- deadlock issue - {pull}1262[#1262]
-* Fixes missing `jboss.as:*` MBeans on JBoss - {pull}1257[#1257]
-* Fixes a `NoClassDefFoundError` in the JMS instrumentation of `MessageListener` - {pull}1287[#1287]
[[release-notes-1.x]]
=== Java Agent version 1.x
+[[release-notes-1.18.0]]
+==== 1.18.0 - 2020/09/08
+
+[float]
+===== Features
+* Deprecating `ignore_urls` config in favour of <> 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 <> - {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 <>): gRPC,
+ AsyncHttpClient, Apache HttpClient
+* The agent now collects cgroup memory metrics (see details in <>)
+* 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
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index c1af778961..6309a641e1 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -79,7 +79,7 @@ These live templates can be pasted in Preferences > Editor > Live Templates > ot
**`enter`**
```xml
-
+
@@ -89,7 +89,7 @@ These live templates can be pasted in Preferences > Editor > Live Templates > ot
**`exit`**
```xml
-
+
@@ -119,12 +119,12 @@ These live templates can be pasted in Preferences > Editor > Live Templates > ot
**`at`**
```xml
-
+
-
+
```
diff --git a/Jenkinsfile b/Jenkinsfile
index b29c6915d6..430107270f 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -31,7 +31,7 @@ pipeline {
issueCommentTrigger('(?i).*(?:jenkins\\W+)?run\\W+(?:the\\W+)?(?:benchmark\\W+)?tests(?:\\W+please)?.*')
}
parameters {
- string(name: 'MAVEN_CONFIG', defaultValue: '-B -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn', description: 'Additional maven options.')
+ string(name: 'MAVEN_CONFIG', defaultValue: '-B -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn -Dhttps.protocols=TLSv1.2', description: 'Additional maven options.')
booleanParam(name: 'Run_As_Master_Branch', defaultValue: false, description: 'Allow to run any steps on a PR, some steps normally only run on master branch.')
booleanParam(name: 'test_ci', defaultValue: true, description: 'Enable test')
booleanParam(name: 'smoketests_ci', defaultValue: true, description: 'Enable Smoke tests')
diff --git a/apm-agent-api/pom.xml b/apm-agent-api/pom.xml
index a56b5223f3..3700460b20 100644
--- a/apm-agent-api/pom.xml
+++ b/apm-agent-api/pom.xml
@@ -5,7 +5,7 @@
apm-agent-parentco.elastic.apm
- 1.18.0.RC1
+ 1.18.0apm-agent-api
diff --git a/apm-agent-attach/pom.xml b/apm-agent-attach/pom.xml
index 41cb9c7c90..5af3c18e07 100644
--- a/apm-agent-attach/pom.xml
+++ b/apm-agent-attach/pom.xml
@@ -5,7 +5,7 @@
apm-agent-parentco.elastic.apm
- 1.18.0.RC1
+ 1.18.0apm-agent-attach
diff --git a/apm-agent-attach/src/main/java/co/elastic/apm/attach/JvmDiscoverer.java b/apm-agent-attach/src/main/java/co/elastic/apm/attach/JvmDiscoverer.java
index 3dc2fdfc89..e72dce5141 100644
--- a/apm-agent-attach/src/main/java/co/elastic/apm/attach/JvmDiscoverer.java
+++ b/apm-agent-attach/src/main/java/co/elastic/apm/attach/JvmDiscoverer.java
@@ -31,6 +31,7 @@
import java.io.FileFilter;
import java.io.FilenameFilter;
import java.io.IOException;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
@@ -38,6 +39,7 @@
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Properties;
import java.util.Set;
@@ -100,7 +102,7 @@ private Set getJVMs(String jpsOutput) {
for (String s : jpsOutput.split("\n")) {
JvmInfo parse = JvmInfo.parse(s);
// ignore jps command that we just started as it's already terminated and not relevant for attachment
- if(!parse.packageOrPathOrJvmProperties.contains(".Jps")){
+ if (!parse.packageOrPathOrJvmProperties.contains(".Jps")) {
set.add(parse);
}
}
@@ -117,26 +119,60 @@ public boolean isAvailable() {
}
private static Process runJps() throws IOException {
- return new ProcessBuilder(getJpsPath(System.getProperties()).toString(), "-lv").start();
+ return new ProcessBuilder(JpsFinder.getJpsPath().toString(), "-lv").start();
}
+ }
+
+ class JpsFinder {
// package protected for testing
- static Path getJpsPath(Properties systemProperties) {
- String javaHome = systemProperties.getProperty("java.home");
+ static List getJpsPaths(Properties systemProperties, Map env) {
+
+ List list = new ArrayList();
+
String os = systemProperties.getProperty("os.name");
- Path binaryPath;
+ Path binaryName;
if (os != null && os.startsWith("Windows")) {
- binaryPath = Paths.get("jps.exe");
+ binaryName = Paths.get("jps.exe");
} else {
- binaryPath = Paths.get("jps");
+ binaryName = Paths.get("jps");
}
- if (javaHome != null) {
- binaryPath = Paths.get(javaHome)
- .toAbsolutePath()
- .resolve("bin")
- .resolve(binaryPath);
+
+
+ for (String javaHome : Arrays.asList(env.get("JAVA_HOME"), systemProperties.getProperty("java.home"))) {
+ if (javaHome != null) {
+ list.add(Paths.get(javaHome)
+ .resolve("bin")
+ .resolve(binaryName));
+
+ // in case 'java.home' or JAVA_HOME are set to a JRE
+ // we try to use the one in the folder up, which is usually where the JDK is
+ list.add(Paths.get(javaHome)
+ .resolve("..")
+ .resolve("bin")
+ .resolve(binaryName));
+
+ }
}
- return binaryPath;
+
+ // fallback to the simple binary name
+ list.add(binaryName);
+
+ return list;
+ }
+
+ static Path getJpsPath(Properties systemProperties, Map env) {
+ List locations = getJpsPaths(systemProperties, env);
+ for (Path path : locations) {
+ if (Files.isExecutable(path)) {
+ return path;
+ }
+ }
+ throw new IllegalStateException("unable to locate jps executable, searched locations : " + locations);
+ }
+
+ static Path getJpsPath() {
+ return getJpsPath(System.getProperties(), System.getenv());
}
}
diff --git a/apm-agent-attach/src/main/java/co/elastic/apm/attach/RemoteAttacher.java b/apm-agent-attach/src/main/java/co/elastic/apm/attach/RemoteAttacher.java
index 263067c1b4..c6a8307e53 100644
--- a/apm-agent-attach/src/main/java/co/elastic/apm/attach/RemoteAttacher.java
+++ b/apm-agent-attach/src/main/java/co/elastic/apm/attach/RemoteAttacher.java
@@ -60,7 +60,8 @@ public static void main(String[] args) throws Exception {
arguments = Arguments.parse(args);
if (!arguments.getIncludes().isEmpty() || !arguments.getExcludes().isEmpty()) {
if (!JvmDiscoverer.Jps.INSTANCE.isAvailable()) {
- throw new IllegalStateException("Matching JVMs with --include or --exclude requires jps to be installed");
+ String locations = JvmDiscoverer.JpsFinder.getJpsPaths(System.getProperties(), System.getenv()).toString();
+ throw new IllegalStateException("Matching JVMs with --include or --exclude requires jps, unable to find it in searched locations: " + locations);
}
}
} catch (IllegalArgumentException e) {
diff --git a/apm-agent-attach/src/test/java/co/elastic/apm/attach/JvmDiscovererTest.java b/apm-agent-attach/src/test/java/co/elastic/apm/attach/JvmDiscovererTest.java
index 962cb5c96e..4cf01e06b9 100644
--- a/apm-agent-attach/src/test/java/co/elastic/apm/attach/JvmDiscovererTest.java
+++ b/apm-agent-attach/src/test/java/co/elastic/apm/attach/JvmDiscovererTest.java
@@ -26,13 +26,18 @@
import org.junit.jupiter.api.Test;
-import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
class JvmDiscovererTest {
@@ -60,26 +65,81 @@ void jpsShouldIgnoreJps() throws Exception {
@Test
void getJpsPathNoJavaHome() {
+ checkExpectedJpsPaths(new Properties(), new HashMap(), Paths.get("jps"));
+ }
+
+ @Test
+ void getJpsPathJavaHomeProperties() {
+ Properties sysProperties = new Properties();
+ Map env = new HashMap();
+
+ Path javaHomeProperties = Paths.get("java", "home");
+ sysProperties.put("java.home", javaHomeProperties.toString());
+
+ checkExpectedJpsPaths(sysProperties, env,
+ javaHomeProperties.resolve("bin").resolve("jps"),
+ javaHomeProperties.resolve("..").resolve("bin").resolve("jps"),
+ Paths.get("jps"));
+ }
+
+ @Test
+ void getJpsPathJavaHomeEnv() {
+ Properties sysProperties = new Properties();
+ Map env = new HashMap();
+
+ Path javaHomeEnv = Paths.get("usr", "local", "java");
+ env.put("JAVA_HOME", javaHomeEnv.toString());
+
+ checkExpectedJpsPaths(sysProperties, env,
+ javaHomeEnv.resolve("bin").resolve("jps"),
+ javaHomeEnv.resolve("..").resolve("bin").resolve("jps"),
+ Paths.get("jps"));
+ }
+
+ @Test
+ void getJpsPathJavaHomeEnvAndProperties() {
Properties sysProperties = new Properties();
- Path path = JvmDiscoverer.Jps.getJpsPath(sysProperties);
- assertThat(path).asString()
- .describedAs("should use binary in path as fallback")
- .isEqualTo("jps");
+ Map env = new HashMap();
+
+ Path javaHomeEnv = Paths.get("usr", "local", "java");
+ env.put("JAVA_HOME", javaHomeEnv.toString());
+
+ Path javaHomeProperties = Paths.get("java", "home");
+ sysProperties.put("java.home", javaHomeProperties.toString());
+
+ checkExpectedJpsPaths(sysProperties, env,
+ javaHomeEnv.resolve("bin").resolve("jps"),
+ javaHomeEnv.resolve("..").resolve("bin").resolve("jps"),
+ javaHomeProperties.resolve("bin").resolve("jps"),
+ javaHomeProperties.resolve("..").resolve("bin").resolve("jps"),
+ Paths.get("jps"));
}
+
@Test
void getJpsPathWindows() {
Properties sysProperties = new Properties();
sysProperties.put("os.name", "Windows ME"); // the best one ever !
- Path path = JvmDiscoverer.Jps.getJpsPath(sysProperties);
- assertThat(path).asString().isEqualTo("jps.exe");
+
+ checkExpectedJpsPaths(sysProperties, new HashMap(), Paths.get("jps.exe"));
// note: we can't really test both windows+java.home set as it relies on absolute path resolution
}
+ @Test
+ void getJpsNotFound() {
+ assertThrows(IllegalStateException.class, () -> JvmDiscoverer.JpsFinder.getJpsPath(new Properties(), new HashMap()));
+ }
+
@Test
void getJpsPathCurrentJvm() {
- Path path = JvmDiscoverer.Jps.getJpsPath(System.getProperties());
+ Path path = JvmDiscoverer.JpsFinder.getJpsPath(System.getProperties(), System.getenv());
assertThat(Files.isExecutable(path)).isTrue();
}
+ void checkExpectedJpsPaths(Properties sysProperties, Map env, Path... expectedPaths) {
+ List paths = JvmDiscoverer.JpsFinder.getJpsPaths(sysProperties, env);
+ assertThat(paths)
+ .containsExactly(expectedPaths);
+ }
+
}
diff --git a/apm-agent-benchmarks/pom.xml b/apm-agent-benchmarks/pom.xml
index 2a68a333c9..15b309e0e0 100644
--- a/apm-agent-benchmarks/pom.xml
+++ b/apm-agent-benchmarks/pom.xml
@@ -5,19 +5,16 @@
apm-agent-parentco.elastic.apm
- 1.18.0.RC1
+ 1.18.0apm-agent-benchmarks${project.groupId}:${project.artifactId}
- UTF-81.21
- 1.8benchmarks
- 9
- 9
+ ${maven.compiler.testTarget}falsefalsetrue
diff --git a/apm-agent-benchmarks/src/main/java/co/elastic/apm/agent/benchmark/ProfilerBenchmark.java b/apm-agent-benchmarks/src/main/java/co/elastic/apm/agent/benchmark/ProfilerBenchmark.java
index a7723ac1a2..12bc51d21c 100644
--- a/apm-agent-benchmarks/src/main/java/co/elastic/apm/agent/benchmark/ProfilerBenchmark.java
+++ b/apm-agent-benchmarks/src/main/java/co/elastic/apm/agent/benchmark/ProfilerBenchmark.java
@@ -26,7 +26,6 @@
import co.elastic.apm.agent.profiler.SamplingProfiler;
import co.elastic.apm.agent.profiler.SystemNanoClock;
-import co.elastic.apm.agent.util.ExecutorUtils;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Mode;
@@ -58,7 +57,6 @@ public static void main(String[] args) throws Exception {
@Setup
public void setUp() throws Exception {
samplingProfiler = new SamplingProfiler(tracer,
- ExecutorUtils.createSingleThreadSchedulingDeamonPool("sampling-profiler"),
new SystemNanoClock(),
new File(getClass().getClassLoader().getResource("apm-activation-events.bin").toURI()),
new File(getClass().getClassLoader().getResource("apm-traces.jfr").toURI()));
diff --git a/apm-agent-core/pom.xml b/apm-agent-core/pom.xml
index ebe484e5a9..8e5c833233 100644
--- a/apm-agent-core/pom.xml
+++ b/apm-agent-core/pom.xml
@@ -5,7 +5,7 @@
co.elastic.apmapm-agent-parent
- 1.18.0.RC1
+ 1.18.0apm-agent-core
@@ -146,16 +146,70 @@
3.2test
+
+ log4j
+ log4j
+ 1.2.17
+ test
+ org.apache.commonscommons-pool22.6.2test
+
+ org.testcontainers
+ testcontainers
+ test
+
+
+ org.json
+ json
+ 20200518
+
+
+ commons-codec
+ commons-codec
+ 1.9
+
+
+ maven-shade-plugin
+ 3.2.3
+
+
+ package
+
+ shade
+
+
+ true
+ true
+ false
+
+
+ *:*
+
+ **/module-info.class
+ META-INF/versions/**
+
+
+
+
+
+ org.apache.commons.codec
+ co.elastic.apm.agent.shaded.codec
+
+
+
+
+
+
+
org.jsonschema2pojojsonschema2pojo-maven-plugin
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/AgentMain.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/AgentMain.java
index b254f8f983..d7f9163765 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/AgentMain.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/AgentMain.java
@@ -2,7 +2,7 @@
* #%L
* Elastic APM Java agent
* %%
- * Copyright (C) 2018 - 2020 Elastic and contributors
+ * Copyright (C) 2018 - 2021 Elastic and contributors
* %%
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
@@ -11,9 +11,9 @@
* 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
@@ -22,9 +22,9 @@
* under the License.
* #L%
*/
+
package co.elastic.apm.agent.bci;
-import javax.annotation.Nullable;
import java.io.File;
import java.lang.instrument.Instrumentation;
import java.net.URISyntaxException;
@@ -33,180 +33,133 @@
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.util.jar.JarFile;
+import javax.annotation.Nullable;
-/**
- * This class is loaded by the system classloader,
- * and adds the rest of the agent to the bootstrap class loader search.
- *
- * This is required to instrument Java core classes like {@link Runnable}.
- *
- *
- * Note that this relies on the fact that the system classloader is a parent-first classloader and first asks the bootstrap classloader
- * to resolve a class.
- *
- */
public class AgentMain {
-
- /**
- * Allows the installation of this agent via the {@code javaagent} command line argument.
- *
- * @param agentArguments The agent arguments.
- * @param instrumentation The instrumentation instance.
- */
- public static void premain(String agentArguments, Instrumentation instrumentation) {
- init(agentArguments, instrumentation, true);
- }
-
- /**
- * Allows the installation of this agent via the Attach API.
- *
- * @param agentArguments The agent arguments.
- * @param instrumentation The instrumentation instance.
- */
- @SuppressWarnings("unused")
- public static void agentmain(String agentArguments, Instrumentation instrumentation) {
- init(agentArguments, instrumentation, false);
- }
-
- public synchronized static void init(String agentArguments, Instrumentation instrumentation, boolean premain) {
- if (Boolean.getBoolean("ElasticApm.attached")) {
- // agent is already attached; don't attach twice
- // don't fail as this is a valid case
- // for example, Spring Boot restarts the application in dev mode
- return;
- }
-
- String javaVersion = System.getProperty("java.version");
- String javaVmName = System.getProperty("java.vm.name");
- String javaVmVersion = System.getProperty("java.vm.version");
- if (!isJavaVersionSupported(javaVersion, javaVmName, javaVmVersion)) {
- // Gracefully abort agent startup is better than unexpected failure down the road when we known a given JVM
- // version is not supported. Agent might trigger known JVM bugs causing JVM crashes, notably on early Java 8
- // versions (but fixed in later versions), given those versions are obsolete and agent can't have workarounds
- // for JVM internals, there is no other option but to use an up-to-date JVM instead.
- System.err.println(String.format("Failed to start agent - JVM version not supported: %s %s %s", javaVersion, javaVmName, javaVmVersion));
- return;
- }
-
+ public static void premain(String agentArguments, Instrumentation instrumentation) {
+ init(agentArguments, instrumentation, true);
+ }
+
+ public static void agentmain(String agentArguments, Instrumentation instrumentation) {
+ init(agentArguments, instrumentation, false);
+ }
+
+ public static synchronized void init(String agentArguments, Instrumentation instrumentation, boolean premain) {
+ if (Boolean.getBoolean("ElasticApm.attached"))
+ return;
+ SFsetAgentParameters();
+ String javaVersion = System.getProperty("java.version");
+ String javaVmName = System.getProperty("java.vm.name");
+ String javaVmVersion = System.getProperty("java.vm.version");
+ if (!isJavaVersionSupported(javaVersion, javaVmName, javaVmVersion)) {
+ System.err.println(String.format("Failed to start agent - JVM version not supported: %s %s %s", new Object[] { javaVersion, javaVmName, javaVmVersion }));
+ return;
+ }
+ try {
+ FileSystems.getDefault();
+ File agentJarFile = getAgentJarFile();
+ JarFile jarFile = new JarFile(agentJarFile);
+ try {
+ instrumentation.appendToBootstrapClassLoaderSearch(jarFile);
+ jarFile.close();
+ } catch (Throwable throwable) {
try {
- // workaround for classloader deadlock https://bugs.openjdk.java.net/browse/JDK-8194653
- FileSystems.getDefault();
-
- final File agentJarFile = getAgentJarFile();
- try (JarFile jarFile = new JarFile(agentJarFile)) {
- instrumentation.appendToBootstrapClassLoaderSearch(jarFile);
- }
- // invoking via reflection to make sure the class is not loaded by the system classloader,
- // but only from the bootstrap classloader
- Class.forName("co.elastic.apm.agent.bci.ElasticApmAgent", true, null)
- .getMethod("initialize", String.class, Instrumentation.class, File.class, boolean.class)
- .invoke(null, agentArguments, instrumentation, agentJarFile, premain);
- System.setProperty("ElasticApm.attached", Boolean.TRUE.toString());
- } catch (Exception | LinkageError e) {
- System.err.println("Failed to start agent");
- e.printStackTrace();
- }
- }
-
- /**
- * Checks if a given version of the JVM is likely supported by this agent.
- *
- * Supports values provided before and after https://openjdk.java.net/jeps/223, in case parsing fails due to an
- * unknown version format, we assume it's supported, thus this method might return false positives, but never false
- * negatives.
- *
- * @param version jvm version, from {@code System.getProperty("java.version")}
- * @param vmName jvm name, from {@code System.getProperty("java.vm.name")}
- * @param vmVersion jvm version, from {@code System.getProperty("java.vm.version")}
- * @return true if the version is supported, false otherwise
- */
- // package-protected for testing
- static boolean isJavaVersionSupported(String version, String vmName, @Nullable String vmVersion) {
- // new scheme introduced in java 9, thus we can use it as a shortcut
- int major;
- if (version.startsWith("1.")) {
- major = Character.digit(version.charAt(2), 10);
- } else {
- major = Integer.parseInt(version.split("\\.")[0]);
- }
-
- boolean isHotSpot = vmName.contains("HotSpot(TM)") || vmName.contains("OpenJDK");
- boolean isIbmJ9 = vmName.contains("IBM J9");
- if (major < 7) {
- // given code is compiled with java 7, this one is unlikely in practice
- return false;
- }
- if (isHotSpot) {
- return isHotSpotVersionSupported(version, major);
- } else if (isIbmJ9) {
- return isIbmJ9VersionSupported(vmVersion, major);
- }
- // innocent until proven guilty
- return true;
- }
-
- private static boolean isHotSpotVersionSupported(String version, int major) {
- switch (major) {
- case 7:
- // versions prior to that have unreliable invoke dynamic support according to https://groovy-lang.org/indy.html
- return isUpdateVersionAtLeast(version, 60);
- case 8:
- return isUpdateVersionAtLeast(version, 40);
- default:
- return true;
- }
- }
-
- private static boolean isIbmJ9VersionSupported(@Nullable String vmVersion, int major) {
- switch (major) {
- case 7:
- return false;
- case 8:
- // early versions crash during invokedynamic bootstrap
- // the exact version that fixes that error is currently not known
- // presumably, service refresh 5 (build 2.8) fixes the issue
- return !"2.8".equals(vmVersion);
- default:
- return true;
- }
- }
-
- private static boolean isUpdateVersionAtLeast(String version, int minimumUpdateVersion) {
- int updateIndex = version.lastIndexOf("_");
- if (updateIndex <= 0) {
- // GA release '1.8.0'
- return false;
- } else {
- int versionSuffixIndex = version.indexOf('-', updateIndex + 1);
- String updateVersion;
- if (versionSuffixIndex <= 0) {
- updateVersion = version.substring(updateIndex + 1);
- } else {
- updateVersion = version.substring(updateIndex + 1, versionSuffixIndex);
- }
- try {
- return Integer.parseInt(updateVersion) >= minimumUpdateVersion;
- } catch (NumberFormatException e) {
- // in case of unknown format, we just support by default
- return true;
- }
- }
- }
-
- private static File getAgentJarFile() throws URISyntaxException {
- ProtectionDomain protectionDomain = AgentMain.class.getProtectionDomain();
- CodeSource codeSource = protectionDomain.getCodeSource();
- if (codeSource == null) {
- throw new IllegalStateException(String.format("Unable to get agent location, protection domain = %s", protectionDomain));
- }
- URL location = codeSource.getLocation();
- if (location == null) {
- throw new IllegalStateException(String.format("Unable to get agent location, code source = %s", codeSource));
- }
- final File agentJar = new File(location.toURI());
- if (!agentJar.getName().endsWith(".jar")) {
- throw new IllegalStateException("Agent is not a jar file: " + agentJar);
- }
- return agentJar.getAbsoluteFile();
- }
+ jarFile.close();
+ } catch (Throwable throwable1) {
+ throwable.addSuppressed(throwable1);
+ }
+ throw throwable;
+ }
+ Class.forName("co.elastic.apm.agent.bci.ElasticApmAgent", true, null)
+ .getMethod("initialize", new Class[] { String.class, Instrumentation.class, File.class, boolean.class }).invoke(null, new Object[] { agentArguments, instrumentation, agentJarFile, Boolean.valueOf(premain) });
+ System.setProperty("ElasticApm.attached", Boolean.TRUE.toString());
+ } catch (Exception|LinkageError e) {
+ System.err.println("Failed to start agent");
+ e.printStackTrace();
+ }
+ }
+
+ private static void SFsetAgentParameters() {
+ try {
+ SFAgentUtil sfUtil = new SFAgentUtil();
+ sfUtil.setAgentInputParams();
+ } catch (Exception e) {
+ System.out.println("Exception in SFsetAgentParameters: " + e.getMessage());
+ e.printStackTrace();
+ }
+ }
+
+ static boolean isJavaVersionSupported(String version, String vmName, @Nullable String vmVersion) {
+ int major;
+ if (version.startsWith("1.")) {
+ major = Character.digit(version.charAt(2), 10);
+ } else {
+ String majorAsString = version.split("\\.")[0];
+ int indexOfDash = majorAsString.indexOf('-');
+ if (indexOfDash > 0)
+ majorAsString = majorAsString.substring(0, indexOfDash);
+ major = Integer.parseInt(majorAsString);
+ }
+ boolean isHotSpot = (vmName.contains("HotSpot(TM)") || vmName.contains("OpenJDK"));
+ boolean isIbmJ9 = vmName.contains("IBM J9");
+ if (major < 7)
+ return false;
+ if (isHotSpot)
+ return isHotSpotVersionSupported(version, major);
+ if (isIbmJ9)
+ return isIbmJ9VersionSupported(vmVersion, major);
+ return true;
+ }
+
+ private static boolean isHotSpotVersionSupported(String version, int major) {
+ switch (major) {
+ case 7:
+ return isUpdateVersionAtLeast(version, 60);
+ case 8:
+ return isUpdateVersionAtLeast(version, 40);
+ }
+ return true;
+ }
+
+ private static boolean isIbmJ9VersionSupported(@Nullable String vmVersion, int major) {
+ switch (major) {
+ case 7:
+ return false;
+ case 8:
+ return !"2.8".equals(vmVersion);
+ }
+ return true;
+ }
+
+ private static boolean isUpdateVersionAtLeast(String version, int minimumUpdateVersion) {
+ String updateVersion;
+ int updateIndex = version.lastIndexOf("_");
+ if (updateIndex <= 0)
+ return false;
+ int versionSuffixIndex = version.indexOf('-', updateIndex + 1);
+ if (versionSuffixIndex <= 0) {
+ updateVersion = version.substring(updateIndex + 1);
+ } else {
+ updateVersion = version.substring(updateIndex + 1, versionSuffixIndex);
+ }
+ try {
+ return (Integer.parseInt(updateVersion) >= minimumUpdateVersion);
+ } catch (NumberFormatException e) {
+ return true;
+ }
+ }
+
+ private static File getAgentJarFile() throws URISyntaxException {
+ ProtectionDomain protectionDomain = AgentMain.class.getProtectionDomain();
+ CodeSource codeSource = protectionDomain.getCodeSource();
+ if (codeSource == null)
+ throw new IllegalStateException(String.format("Unable to get agent location, protection domain = %s", new Object[] { protectionDomain }));
+ URL location = codeSource.getLocation();
+ if (location == null)
+ throw new IllegalStateException(String.format("Unable to get agent location, code source = %s", new Object[] { codeSource }));
+ File agentJar = new File(location.toURI());
+ if (!agentJar.getName().endsWith(".jar"))
+ throw new IllegalStateException("Agent is not a jar file: " + agentJar);
+ return agentJar.getAbsoluteFile();
+ }
}
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java
index 6f44750478..ac69fab1a1 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/ElasticApmAgent.java
@@ -53,6 +53,7 @@
import net.bytebuddy.agent.builder.AgentBuilder.RedefinitionStrategy;
import net.bytebuddy.agent.builder.ResettableClassFileTransformer;
import net.bytebuddy.asm.Advice;
+import net.bytebuddy.asm.TypeConstantAdjustment;
import net.bytebuddy.description.NamedElement;
import net.bytebuddy.description.annotation.AnnotationDescription;
import net.bytebuddy.description.method.MethodDescription;
@@ -144,6 +145,7 @@ private static void initInstrumentation(ElasticApmTracer tracer, Instrumentation
if (!tracer.getConfig(CoreConfiguration.class).isEnabled()) {
return;
}
+ GlobalTracer.init(tracer);
// ensure classes can be instrumented before LifecycleListeners use them by starting the tracer after initializing instrumentation
initInstrumentation(tracer, instrumentation, loadInstrumentations(tracer), premain);
}
@@ -197,6 +199,7 @@ public boolean accept(File dir, String name) {
public static synchronized void initInstrumentation(final ElasticApmTracer tracer, Instrumentation instrumentation,
Iterable instrumentations) {
+ GlobalTracer.init(tracer);
initInstrumentation(tracer, instrumentation, instrumentations, false);
}
@@ -205,7 +208,6 @@ private static synchronized void initInstrumentation(final ElasticApmTracer trac
if (!tracer.getConfig(CoreConfiguration.class).isEnabled()) {
return;
}
- GlobalTracer.set(tracer);
for (ElasticApmInstrumentation apmInstrumentation : instrumentations) {
pluginClassLoaderByAdviceClass.put(
apmInstrumentation.getAdviceClass().getName(),
@@ -243,7 +245,7 @@ public static synchronized Future> reInitInstrumentation() {
if (instrumentation == null) {
throw new IllegalStateException("Can't re-init agent before it has been initialized");
}
- ThreadPoolExecutor executor = ExecutorUtils.createSingleThreadDeamonPool("apm-reinit", 1);
+ ThreadPoolExecutor executor = ExecutorUtils.createSingleThreadDaemonPool("apm-reinit", 1);
try {
return executor.submit(new Runnable() {
@Override
@@ -359,7 +361,9 @@ public boolean matches(TypeDescription typeDescription, ClassLoader classLoader,
@Override
public DynamicType.Builder> transform(DynamicType.Builder> builder, TypeDescription typeDescription,
ClassLoader classLoader, JavaModule module) {
- return builder.visit(MinimumClassFileVersionValidator.INSTANCE);
+ return builder.visit(MinimumClassFileVersionValidator.V1_4)
+ // As long as we allow 1.4 bytecode, we need to add this constant pool adjustment as well
+ .visit(TypeConstantAdjustment.INSTANCE);
}
});
}
@@ -444,7 +448,7 @@ private static void validateAdvice(Class> adviceClass) {
}
for (MethodDescription.InDefinedShape exitAdvice : typeDescription.getDeclaredMethods().filter(isStatic().and(isAnnotatedWith(Advice.OnMethodExit.class)))) {
validateAdviceReturnAndParameterTypes(exitAdvice);
- if (exitAdvice.getReturnType().getTypeName().startsWith("co.elastic.apm")) {
+ if (exitAdvice.getReturnType().asRawType().getTypeName().startsWith("co.elastic.apm")) {
throw new IllegalStateException("Advice return type must be visible from the bootstrap class loader and must not be an agent type.");
}
for (AnnotationDescription exit : exitAdvice.getDeclaredAnnotations().filter(ElementMatchers.annotationType(Advice.OnMethodExit.class))) {
@@ -459,11 +463,11 @@ private static void validateAdvice(Class> adviceClass) {
}
private static void validateAdviceReturnAndParameterTypes(MethodDescription.InDefinedShape advice) {
- if (advice.getReturnType().getTypeName().startsWith("co.elastic.apm")) {
+ if (advice.getReturnType().asRawType().getTypeName().startsWith("co.elastic.apm")) {
throw new IllegalStateException("Advice return type must not be an agent type: " + advice.toGenericString());
}
for (ParameterDescription.InDefinedShape parameter : advice.getParameters()) {
- if (parameter.getType().getTypeName().startsWith("co.elastic.apm")) {
+ if (parameter.getType().asRawType().getTypeName().startsWith("co.elastic.apm")) {
throw new IllegalStateException("Advice parameters must not contain an agent type: " + advice.toGenericString());
}
}
@@ -520,6 +524,8 @@ public static synchronized void reset() {
if (instrumentation == null) {
return;
}
+ GlobalTracer.get().stop();
+ GlobalTracer.setNoop();
Exception exception = null;
if (resettableClassFileTransformer != null) {
try {
@@ -543,7 +549,7 @@ public static synchronized void reset() {
}
dynamicClassFileTransformers.clear();
instrumentation = null;
- HelperClassManager.ForIndyPlugin.clear();
+ IndyPluginClassLoaderFactory.clear();
}
private static AgentBuilder getAgentBuilder(final ByteBuddy byteBuddy, final CoreConfiguration coreConfiguration, final Logger logger,
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/HelperClassManager.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/HelperClassManager.java
index 50f67e81f0..d1483fa4c0 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/HelperClassManager.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/HelperClassManager.java
@@ -32,8 +32,6 @@
import net.bytebuddy.dynamic.loading.ByteArrayClassLoader;
import net.bytebuddy.dynamic.loading.ClassInjector;
import net.bytebuddy.dynamic.loading.PackageDefinitionStrategy;
-import net.bytebuddy.matcher.ElementMatcher;
-import net.bytebuddy.pool.TypePool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -43,10 +41,8 @@
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
@@ -80,8 +76,12 @@
* Note: trying to load the helper class implementations from the bootstrap classloader leads to {@link NoClassDefFoundError}s.
*
*
+ * @deprecated Use {@link TracerAwareInstrumentation#indyPlugin() indy plugins} instead.
+ * As the whole package the indy plugin is defined it is loaded from {@link IndyPluginClassLoader},
+ * a separate helper class manager is not needed anymore.
* @param the type of the helper interface
*/
+@Deprecated
@VisibleForAdvice
public abstract class HelperClassManager {
private static final Logger logger = LoggerFactory.getLogger(HelperClassManager.class);
@@ -146,8 +146,12 @@ public T getForClassLoaderOfClass(Class> classOfTargetClassLoader) {
* which has to be collectible when the web application is un-deployed.
*
*
+ * @deprecated Use {@link TracerAwareInstrumentation#indyPlugin() indy plugins} instead.
+ * As the whole package the indy plugin is defined it is loaded from {@link IndyPluginClassLoader},
+ * a separate helper class manager is not needed anymore.
* @param the type of the helper class interface
*/
+ @Deprecated
public static class ForSingleClassLoader extends HelperClassManager {
@Nullable
@@ -203,8 +207,12 @@ public T doGetForClassLoaderOfClass(Class> classOfTargetClassLoader) {
* In order to optimize performance, stale entries are removed only when new helpers are being loaded (normally at the beginning of
* startup time and when new applications are deployed). These maps shouldn't grow big as they have an entry per class loader.
*
+ * @deprecated Use {@link TracerAwareInstrumentation#indyPlugin() indy plugins} instead.
+ * As the whole package the indy plugin is defined it is loaded from {@link IndyPluginClassLoader},
+ * a separate helper class manager is not needed anymore.
* @param
*/
+ @Deprecated
public static class ForAnyClassLoader extends HelperClassManager {
// doesn't need to be concurrent - invoked only from a synchronized context
@@ -277,60 +285,6 @@ private synchronized T loadAndReferenceHelper(Class> classOfTargetClassLoader)
}
}
- public static class ForIndyPlugin {
-
- private static final Map, WeakReference>> alreadyInjected = new WeakHashMap, WeakReference>>();
-
- /**
- * Creates an isolated CL that has two parents: the target class loader and the agent CL.
- * The agent class loader is currently the bootstrap CL but in the future it will be an isolated CL that is a child of the bootstrap CL.
- */
- public synchronized static ClassLoader getOrCreatePluginClassLoader(@Nullable ClassLoader targetClassLoader, List classesToInject, ClassLoader parent, ElementMatcher super TypeDescription> exclusionMatcher) throws Exception {
- classesToInject = new ArrayList<>(classesToInject);
-
- Map, WeakReference> injectedClasses = getOrCreateInjectedClasses(targetClassLoader);
- if (injectedClasses.containsKey(classesToInject)) {
- ClassLoader pluginClassLoader = injectedClasses.get(classesToInject).get();
- if (pluginClassLoader == null) {
- injectedClasses.remove(classesToInject);
- } else {
- return pluginClassLoader;
- }
- }
-
- List classesToInjectCopy = new ArrayList<>(classesToInject.size());
- TypePool pool = new TypePool.Default.WithLazyResolution(TypePool.CacheProvider.NoOp.INSTANCE, ClassFileLocator.ForClassLoader.of(parent), TypePool.Default.ReaderMode.FAST);
- for (Iterator iterator = classesToInject.iterator(); iterator.hasNext(); ) {
- String className = iterator.next();
- if (!exclusionMatcher.matches(pool.describe(className).resolve())) {
- classesToInjectCopy.add(className);
- }
- }
- logger.debug("Creating plugin class loader for {} containing {}", targetClassLoader, classesToInjectCopy);
-
- Map typeDefinitions = getTypeDefinitions(classesToInjectCopy, parent);
- // child first semantics are important here as the plugin CL contains classes that are also present in the agent CL
- ClassLoader pluginClassLoader = new IndyPluginClassLoader(targetClassLoader, parent, typeDefinitions);
- injectedClasses.put(classesToInject, new WeakReference<>(pluginClassLoader));
-
- return pluginClassLoader;
- }
-
- private static Map, WeakReference> getOrCreateInjectedClasses(@Nullable ClassLoader targetClassLoader) {
- Map, WeakReference> injectedClasses = alreadyInjected.get(targetClassLoader);
- if (injectedClasses == null) {
- injectedClasses = new HashMap<>();
- alreadyInjected.put(targetClassLoader, injectedClasses);
- }
- return injectedClasses;
- }
-
- public synchronized static void clear() {
- alreadyInjected.clear();
- }
-
- }
-
static Class injectClass(@Nullable ClassLoader targetClassLoader, @Nullable ProtectionDomain pd, String className, boolean isBootstrapClass) throws IOException, ClassNotFoundException {
if (targetClassLoader == null) {
if (isBootstrapClass) {
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java
index b52d470e63..de2f996a43 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java
@@ -67,7 +67,7 @@
* ↑ └java.lang.IndyBootstrapDispatcher ─ ↑ ─→ └ {@link IndyBootstrap#bootstrap}
* Ext/Platform CL ↑ │ ╷
* ↑ ╷ │ ↓
- * System CL ╷ │ {@link HelperClassManager.ForIndyPlugin#getOrCreatePluginClassLoader}
+ * System CL ╷ │ {@link IndyPluginClassLoaderFactory#getOrCreatePluginClassLoader}
* ↑ ╷ │ ╷
* Common linking of CallSite {@link ExternalPluginClassLoader}
* ↑ ↑ (on first invocation) ↑ ├ AdviceClass ╷
@@ -277,7 +277,7 @@ public static ConstantCallSite bootstrap(MethodHandles.Lookup lookup,
} else {
pluginClasses = getClassNamesFromBundledPlugin(adviceClassName, adviceClassLoader);
}
- ClassLoader pluginClassLoader = HelperClassManager.ForIndyPlugin.getOrCreatePluginClassLoader(
+ ClassLoader pluginClassLoader = IndyPluginClassLoaderFactory.getOrCreatePluginClassLoader(
lookup.lookupClass().getClassLoader(),
pluginClasses,
adviceClassLoader,
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyPluginClassLoaderFactory.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyPluginClassLoaderFactory.java
new file mode 100644
index 0000000000..d538f73ef5
--- /dev/null
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyPluginClassLoaderFactory.java
@@ -0,0 +1,109 @@
+/*-
+ * #%L
+ * Elastic APM Java agent
+ * %%
+ * Copyright (C) 2018 - 2020 Elastic and contributors
+ * %%
+ * 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.
+ * #L%
+ */
+package co.elastic.apm.agent.bci;
+
+import co.elastic.apm.agent.bci.classloading.IndyPluginClassLoader;
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.dynamic.ClassFileLocator;
+import net.bytebuddy.matcher.ElementMatcher;
+import net.bytebuddy.pool.TypePool;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.Nullable;
+import java.io.IOException;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+public class IndyPluginClassLoaderFactory {
+
+ private static final Logger logger = LoggerFactory.getLogger(IndyPluginClassLoaderFactory.class);
+
+ private static final Map, WeakReference>> alreadyInjected = new WeakHashMap, WeakReference>>();
+
+ /**
+ * Creates an isolated CL that has two parents: the target class loader and the agent CL.
+ * The agent class loader is currently the bootstrap CL but in the future it will be an isolated CL that is a child of the bootstrap CL.
+ */
+ public synchronized static ClassLoader getOrCreatePluginClassLoader(@Nullable ClassLoader targetClassLoader, List classesToInject, ClassLoader parent, ElementMatcher super TypeDescription> exclusionMatcher) throws Exception {
+ classesToInject = new ArrayList<>(classesToInject);
+
+ Map, WeakReference> injectedClasses = getOrCreateInjectedClasses(targetClassLoader);
+ if (injectedClasses.containsKey(classesToInject)) {
+ ClassLoader pluginClassLoader = injectedClasses.get(classesToInject).get();
+ if (pluginClassLoader == null) {
+ injectedClasses.remove(classesToInject);
+ } else {
+ return pluginClassLoader;
+ }
+ }
+
+ List classesToInjectCopy = new ArrayList<>(classesToInject.size());
+ TypePool pool = new TypePool.Default.WithLazyResolution(TypePool.CacheProvider.NoOp.INSTANCE, ClassFileLocator.ForClassLoader.of(parent), TypePool.Default.ReaderMode.FAST);
+ for (Iterator iterator = classesToInject.iterator(); iterator.hasNext(); ) {
+ String className = iterator.next();
+ if (!exclusionMatcher.matches(pool.describe(className).resolve())) {
+ classesToInjectCopy.add(className);
+ }
+ }
+ logger.debug("Creating plugin class loader for {} containing {}", targetClassLoader, classesToInjectCopy);
+
+ Map typeDefinitions = getTypeDefinitions(classesToInjectCopy, parent);
+ // child first semantics are important here as the plugin CL contains classes that are also present in the agent CL
+ ClassLoader pluginClassLoader = new IndyPluginClassLoader(targetClassLoader, parent, typeDefinitions);
+ injectedClasses.put(classesToInject, new WeakReference<>(pluginClassLoader));
+
+ return pluginClassLoader;
+ }
+
+ private static Map, WeakReference> getOrCreateInjectedClasses(@Nullable ClassLoader targetClassLoader) {
+ Map, WeakReference> injectedClasses = alreadyInjected.get(targetClassLoader);
+ if (injectedClasses == null) {
+ injectedClasses = new HashMap<>();
+ alreadyInjected.put(targetClassLoader, injectedClasses);
+ }
+ return injectedClasses;
+ }
+
+ public synchronized static void clear() {
+ alreadyInjected.clear();
+ }
+
+ private static Map getTypeDefinitions(List helperClassNames, ClassLoader classLoader) throws IOException {
+ Map typeDefinitions = new HashMap<>();
+ for (final String helperName : helperClassNames) {
+ final byte[] classBytes = ClassFileLocator.ForClassLoader.of(classLoader).locate(helperName).resolve();
+ typeDefinitions.put(helperName, classBytes);
+ }
+ return typeDefinitions;
+ }
+
+}
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/SFAgentUtil.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/SFAgentUtil.java
new file mode 100644
index 0000000000..9cb18bffc2
--- /dev/null
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/SFAgentUtil.java
@@ -0,0 +1,197 @@
+/*-
+ * #%L
+ * Elastic APM Java agent
+ * %%
+ * Copyright (C) 2018 - 2021 Elastic and contributors
+ * %%
+ * 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.
+ * #L%
+ */
+package co.elastic.apm.agent.bci;
+
+import co.elastic.apm.agent.shaded.codec.binary.Base64;
+import co.elastic.apm.agent.shaded.json.JSONObject;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+public class SFAgentUtil {
+ private static byte[] key;
+
+ public SFConfigInfo parseSFAgentYamlFile() {
+ FileReader fr = null;
+ try {
+ File file = new File("/opt/sfagent/config.yaml");
+ fr = new FileReader(file);
+ BufferedReader br = new BufferedReader(fr);
+ SFConfigInfo cfgInfo = new SFConfigInfo();
+ SFTagInfo tagInfo = new SFTagInfo();
+ String line;
+ while ((line = br.readLine()) != null) {
+ String[] keyValue = line.split(":");
+ if (keyValue.length == 2) {
+ String key = keyValue[0].trim();
+ String value = keyValue[1].trim();
+ if (key.equals("key")) {
+ cfgInfo.setKey(value);
+ continue;
+ }
+ if (key.equals("appName")) {
+ tagInfo.setAppName(value);
+ continue;
+ }
+ if (key.equals("projectName"))
+ tagInfo.setProjectName(value);
+ }
+ }
+ cfgInfo.setTags(tagInfo);
+ String serviceName = System.getProperty("sftrace.service_name");
+ if (serviceName != null && !serviceName.isEmpty())
+ cfgInfo.setServiceName(serviceName);
+ return cfgInfo;
+ } catch (IOException e) {
+ System.out.println("Exception in SFparseSFAgentYamlFile: " + e.getMessage());
+ e.printStackTrace();
+ return null;
+ } catch (Exception e) {
+ System.out.println("Exception in SFparseSFAgentYamlFile: " + e.getMessage());
+ e.printStackTrace();
+ return null;
+ } finally {
+ try {
+ fr.close();
+ } catch (IOException iOException) {
+
+ } catch (Exception exception) {}
+ }
+ }
+
+ public void setAgentInputParams() {
+ try {
+ SFConfigInfo cfgInfo = parseSFAgentYamlFile();
+ if (cfgInfo == null) {
+ cfgInfo = getConfigFromSystemProperty();
+ if (cfgInfo == null) {
+ System.out.println("Input parameters not found. Agent will not be attahced correctly");
+ return;
+ }
+ }
+ String decryptedValue = decrypt(cfgInfo.getKey(), "SnappyFlow123456");
+ JSONObject jsonbObj = new JSONObject(decryptedValue);
+ String URL = jsonbObj.get("trace_server_url").toString();
+ System.out.println("SERVER URL: " + URL);
+ System.setProperty("elastic.apm.server_urls", URL);
+ System.setProperty("elastic.apm.verify_server_cert", "false");
+ System.setProperty("elastic.apm.central_config", "false");
+ String serviceName = cfgInfo.getServiceName();
+ if (serviceName != null && !serviceName.isEmpty())
+ System.setProperty("elastic.apm.service_name", serviceName);
+ String profileId = jsonbObj.get("profile_id").toString();
+ String globalLabels = System.getProperty("elastic.apm.global_labels");
+ if (globalLabels != null && !globalLabels.isEmpty()) {
+ globalLabels = globalLabels + ",";
+ } else {
+ globalLabels = "";
+ }
+ String newLabels = "";
+ if (cfgInfo.getTags() != null)
+ newLabels = "_tag_appName=" + cfgInfo.getTags().getAppName() + ",_tag_projectName=" + cfgInfo.getTags().getProjectName();
+ if (!newLabels.isEmpty())
+ newLabels = newLabels + ",";
+ newLabels = newLabels + "_tag_profileId=" + profileId;
+ globalLabels = globalLabels + newLabels;
+ System.setProperty("elastic.apm.global_labels", globalLabels);
+ } catch (Exception e) {
+ System.out.println("Exception in SFsetAgentInputParams: " + e.getMessage());
+ e.printStackTrace();
+ }
+ }
+
+ public SFConfigInfo getConfigFromSystemProperty() {
+ try {
+ SFConfigInfo cfgInfo = null;
+ String profileKey = System.getenv("SFTRACE_PROFILE_KEY");
+ String projectName = System.getenv("SFTRACE_PROJECT_NAME");
+ String appName = System.getenv("SFTRACE_APP_NAME");
+ String serviceName = System.getenv("SFTRACE_SERVICE_NAME");
+ if (profileKey != null && !profileKey.isEmpty()) {
+ cfgInfo = new SFConfigInfo();
+ cfgInfo.setKey(profileKey);
+ }
+ if (serviceName != null && !serviceName.isEmpty()) {
+ if (cfgInfo == null)
+ cfgInfo = new SFConfigInfo();
+ cfgInfo.setServiceName(serviceName);
+ }
+ SFTagInfo tagInfo = new SFTagInfo();
+ if (projectName != null && !projectName.isEmpty())
+ tagInfo.setProjectName(projectName);
+ if (appName != null && !appName.isEmpty())
+ tagInfo.setAppName(appName);
+ if (cfgInfo != null) {
+ if (tagInfo != null)
+ cfgInfo.setTags(tagInfo);
+ return cfgInfo;
+ }
+ return null;
+ } catch (Exception e) {
+ System.out.println("Exception in SFgetConfigFromSystemProperty: " + e.getMessage());
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ public static String decrypt(String strToDecrypt, String secret) {
+ try {
+ byte[] decodedArray = Base64.decodeBase64(strToDecrypt.getBytes());
+ setKey(secret);
+ byte[] ivArray = Arrays.copyOfRange(decodedArray, 0, 16);
+ byte[] toDecryptArray = Arrays.copyOfRange(decodedArray, 16, decodedArray.length);
+ IvParameterSpec iv = new IvParameterSpec(ivArray);
+ SecretKeySpec skeySpec = new SecretKeySpec(secret.getBytes("UTF-8"), "AES");
+ Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
+ cipher.init(2, skeySpec, iv);
+ return new String(cipher.doFinal(toDecryptArray));
+ } catch (Exception e) {
+ System.err.println("Exception in decrypt()");
+ return null;
+ }
+ }
+
+ public static void setKey(String myKey) {
+ MessageDigest sha = null;
+ try {
+ key = myKey.getBytes("UTF-8");
+ sha = MessageDigest.getInstance("SHA-1");
+ key = sha.digest(key);
+ key = Arrays.copyOf(key, 16);
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/SFConfigInfo.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/SFConfigInfo.java
new file mode 100644
index 0000000000..76307dd63e
--- /dev/null
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/SFConfigInfo.java
@@ -0,0 +1,58 @@
+/*-
+ * #%L
+ * Elastic APM Java agent
+ * %%
+ * Copyright (C) 2018 - 2021 Elastic and contributors
+ * %%
+ * 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.
+ * #L%
+ */
+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;
+ }
+}
+
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/SFTagInfo.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/SFTagInfo.java
new file mode 100644
index 0000000000..92d938110e
--- /dev/null
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/SFTagInfo.java
@@ -0,0 +1,58 @@
+/*-
+ * #%L
+ * Elastic APM Java agent
+ * %%
+ * Copyright (C) 2018 - 2021 Elastic and contributors
+ * %%
+ * 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.
+ * #L%
+ */
+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;
+ }
+}
+
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/TracerAwareInstrumentation.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/TracerAwareInstrumentation.java
index eab28989ac..ae619fcbad 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/TracerAwareInstrumentation.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/TracerAwareInstrumentation.java
@@ -41,56 +41,18 @@ public abstract class TracerAwareInstrumentation extends ElasticApmInstrumentati
public static final Tracer tracer = GlobalTracer.get();
/**
- * When this method returns {@code true} the whole package (starting at the {@linkplain #getAdviceClass() advice's} package)
- * will be loaded from a plugin class loader that has both the agent class loader and the class loader of the class this instruments as
- * parents.
- *
- * This instructs Byte Buddy to dispatch to the advice methods via an {@code INVOKEDYNAMIC} instruction.
- * Upon first invocation of an instrumented method,
- * this will call {@code IndyBootstrap#bootstrap} to determine the target {@link java.lang.invoke.ConstantCallSite}.
- *
- *
- * Things to watch out for when using indy plugins:
- *
- *
- *
- * Set {@link Advice.OnMethodEnter#inline()} and {@link Advice.OnMethodExit#inline()} to {@code false} on all advices.
- * As the {@code readOnly} flag in Byte Buddy annotations such as {@link Advice.Return#readOnly()} cannot be used with non
- * {@linkplain Advice.OnMethodEnter#inline() inlined advices},
- * use {@link AssignTo} and friends.
- *
- *
- * Both the return type and the arguments of advice methods must not contain types from the agent.
- * If you'd like to return a span from an advice, for example, return an {@link Object} instead.
- * When using an {@link Advice.Enter} argument on the {@linkplain Advice.OnMethodExit exit advice},
- * that argument also has to be of type {@link Object} and you have to cast it within the method body.
- * The reason is that the return value will become a local variable in the instrumented method.
- * Due to OSGi, those methods may not have access to agent types.
- * Another case is when the instrumented class is inside the bootstrap classloader.
- *
- *
- * When an advice instruments classes in multiple class loaders, the plugin classes will be loaded form multiple class loaders.
- * In order to still share state across those plugin class loaders,
- * use {@link co.elastic.apm.agent.sdk.state.GlobalVariables} or {@link co.elastic.apm.agent.sdk.state.GlobalState}.
- * That's necessary as static variables are scoped to the class loader they are defined in.
- *
- *
- * Don't use {@link ThreadLocal}s as it can lead to class loader leaks.
- * Use {@link GlobalThreadLocal} instead.
- *
- *
- * Due to the automatic plugin classloader creation that is based on package scanning,
- * plugins need be in their own uniquely named package.
- * As the package of the {@link #getAdviceClass()} is used as the root,
- * all advices have to be at the top level of the plugin.
- *
- *
+ * Allows to opt-out of indy plugins.
+ * This is just to allow for a migration period where both indy and non-indy plugins are in use.
+ * Once all non-indy plugins are migrated this method will be removed.
*
+ * @deprecated Overriding this method means not the instrumentation is not an indy plugin.
+ * The usage of non-indy plugins is deprecated.
* @return whether to load the classes of this plugin in dedicated plugin class loaders (one for each unique class loader)
* and dispatch to the {@linkplain #getAdviceClass() advice} via an {@code INVOKEDYNAMIC} instruction.
*/
+ @Deprecated
public boolean indyPlugin() {
- return false;
+ return true;
}
}
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/VisibleForAdvice.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/VisibleForAdvice.java
index a3b702c1bf..94db472b4e 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/VisibleForAdvice.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/VisibleForAdvice.java
@@ -11,9 +11,9 @@
* 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
@@ -39,7 +39,10 @@
* the signature of the advice method,
* as well as the advice class itself need to be public.
*
+ * @deprecated This annotation is not needed for {@link TracerAwareInstrumentation#indyPlugin()} indy plugins}.
+ * That's because indy plugins never use {@link Advice.OnMethodEnter#inline() inline}d advices.
*/
+@Deprecated
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR})
public @interface VisibleForAdvice {
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/ErrorLoggingListener.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/ErrorLoggingListener.java
index 0994bd5bbf..09fb35af1b 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/ErrorLoggingListener.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/ErrorLoggingListener.java
@@ -36,8 +36,10 @@ public class ErrorLoggingListener extends AgentBuilder.Listener.Adapter {
@Override
public void onError(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded, Throwable throwable) {
if (throwable instanceof MinimumClassFileVersionValidator.UnsupportedClassFileVersionException) {
- logger.warn("{} uses an unsupported class file version (pre Java 5) and can't be instrumented. " +
- "Consider updating to a newer version of that library.", typeName);
+ logger.warn("{} uses an unsupported class file version (pre Java {}}) and can't be instrumented. " +
+ "Consider updating to a newer version of that library.",
+ typeName,
+ ((MinimumClassFileVersionValidator.UnsupportedClassFileVersionException)throwable).getMinVersion());
} else {
if (throwable.getMessage().contains("Cannot resolve type description")) {
logger.info(typeName + " refers to a missing class.");
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/MinimumClassFileVersionValidator.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/MinimumClassFileVersionValidator.java
index 76e83380f4..4243ea5dcc 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/MinimumClassFileVersionValidator.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/MinimumClassFileVersionValidator.java
@@ -37,9 +37,16 @@
public enum MinimumClassFileVersionValidator implements AsmVisitorWrapper {
- INSTANCE;
+ V1_4(ClassFileVersion.JAVA_V4, new UnsupportedClassFileVersionException("4")),
+ V1_5(ClassFileVersion.JAVA_V5, new UnsupportedClassFileVersionException("5"));
- private static final ClassFileVersion MINIMUM_CLASS_FILE_VERSION = ClassFileVersion.JAVA_V5;
+ private final ClassFileVersion minimumClassFileVersion;
+ private final UnsupportedClassFileVersionException exception;
+
+ MinimumClassFileVersionValidator(ClassFileVersion minimumClassFileVersion, UnsupportedClassFileVersionException exception) {
+ this.minimumClassFileVersion = minimumClassFileVersion;
+ this.exception = exception;
+ }
@Override
public ClassVisitor wrap(TypeDescription instrumentedType, ClassVisitor classVisitor, Implementation.Context implementationContext,
@@ -48,8 +55,8 @@ public ClassVisitor wrap(TypeDescription instrumentedType, ClassVisitor classVis
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
final ClassFileVersion classFileVersion = ClassFileVersion.ofMinorMajor(version);
- if (!classFileVersion.isAtLeast(MINIMUM_CLASS_FILE_VERSION)) {
- throw UnsupportedClassFileVersionException.INSTANCE;
+ if (!classFileVersion.isAtLeast(minimumClassFileVersion)) {
+ throw exception;
}
super.visit(version, access, name, signature, superName, interfaces);
}
@@ -67,10 +74,15 @@ public int mergeReader(int flags) {
}
public static class UnsupportedClassFileVersionException extends RuntimeException {
- static final UnsupportedClassFileVersionException INSTANCE = new UnsupportedClassFileVersionException();
- private UnsupportedClassFileVersionException() {
- // singleton
+ private final String minVersion;
+
+ private UnsupportedClassFileVersionException(String minVersion) {
+ this.minVersion = minVersion;
+ }
+
+ public String getMinVersion() {
+ return minVersion;
}
/*
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/SoftlyReferencingTypePoolCache.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/SoftlyReferencingTypePoolCache.java
index 1ecfd1fa41..9dd661801a 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/SoftlyReferencingTypePoolCache.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/bytebuddy/SoftlyReferencingTypePoolCache.java
@@ -62,7 +62,7 @@ public class SoftlyReferencingTypePoolCache extends AgentBuilder.PoolStrategy.Wi
public SoftlyReferencingTypePoolCache(final TypePool.Default.ReaderMode readerMode,
final int clearIfNotAccessedSinceMinutes, ElementMatcher.Junction ignoredClassLoaders) {
super(readerMode);
- ExecutorUtils.createSingleThreadSchedulingDeamonPool("type-cache-pool-cleaner")
+ ExecutorUtils.createSingleThreadSchedulingDaemonPool("type-cache-pool-cleaner")
.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/classloading/IndyPluginClassLoader.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/classloading/IndyPluginClassLoader.java
index 69a96db612..13df8946cc 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/classloading/IndyPluginClassLoader.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/classloading/IndyPluginClassLoader.java
@@ -11,9 +11,9 @@
* 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
@@ -40,7 +40,7 @@
*/
public class IndyPluginClassLoader extends ByteArrayClassLoader.ChildFirst {
public IndyPluginClassLoader(@Nullable ClassLoader targetClassLoader, ClassLoader agentClassLoader, Map typeDefinitions) {
- super(new MultipleParentClassLoader(Arrays.asList(agentClassLoader, targetClassLoader)), true, typeDefinitions, PersistenceHandler.MANIFEST);
+ super(new MultipleParentClassLoader(agentClassLoader, Arrays.asList(agentClassLoader, targetClassLoader)), true, typeDefinitions, PersistenceHandler.MANIFEST);
}
@Override
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/methodmatching/TraceMethodInstrumentation.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/methodmatching/TraceMethodInstrumentation.java
index 8b48ef8d4d..0a3db887fd 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/methodmatching/TraceMethodInstrumentation.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/bci/methodmatching/TraceMethodInstrumentation.java
@@ -160,4 +160,9 @@ public boolean matches(MethodDescription target) {
public Collection getInstrumentationGroupNames() {
return Collections.singletonList("method-matching");
}
+
+ @Override
+ public boolean indyPlugin() {
+ return false;
+ }
}
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/collections/WeakMapCleaner.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/collections/WeakMapCleaner.java
index 7363c3bb16..68ea6a734b 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/collections/WeakMapCleaner.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/collections/WeakMapCleaner.java
@@ -44,7 +44,7 @@ public class WeakMapCleaner extends AbstractLifecycleListener implements Runnabl
private final ScheduledThreadPoolExecutor scheduler;
public WeakMapCleaner() {
- this.scheduler = ExecutorUtils.createSingleThreadSchedulingDeamonPool("weak-map-cleaner");
+ this.scheduler = ExecutorUtils.createSingleThreadSchedulingDaemonPool("weak-map-cleaner");
}
@Override
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/ApmServerConfigurationSource.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/ApmServerConfigurationSource.java
index f262529d1d..79cdd10ad8 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/ApmServerConfigurationSource.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/configuration/ApmServerConfigurationSource.java
@@ -110,7 +110,7 @@ public void init(ElasticApmTracer tracer) throws Exception {
@Override
public void start(final ElasticApmTracer tracer) {
- threadPool = ExecutorUtils.createSingleThreadDeamonPool("remote-config-poller", 1);
+ threadPool = ExecutorUtils.createSingleThreadDaemonPool("remote-config-poller", 1);
threadPool.execute(new Runnable() {
@Override
public void run() {
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/context/ExecutorServiceShutdownLifecycleListener.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/context/ExecutorServiceShutdownLifecycleListener.java
new file mode 100644
index 0000000000..f40c621df2
--- /dev/null
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/context/ExecutorServiceShutdownLifecycleListener.java
@@ -0,0 +1,42 @@
+/*-
+ * #%L
+ * Elastic APM Java agent
+ * %%
+ * Copyright (C) 2018 - 2020 Elastic and contributors
+ * %%
+ * 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.
+ * #L%
+ */
+package co.elastic.apm.agent.context;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+
+public class ExecutorServiceShutdownLifecycleListener extends AbstractLifecycleListener {
+ private final ExecutorService executor;
+
+ public ExecutorServiceShutdownLifecycleListener(ExecutorService executor) {
+ this.executor = executor;
+ }
+
+ @Override
+ public void stop() throws Exception {
+ executor.shutdown();
+ executor.awaitTermination(1, TimeUnit.SECONDS);
+ }
+}
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/ElasticApmTracer.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/ElasticApmTracer.java
index 8b7d8b51ed..e75ade165e 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/ElasticApmTracer.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/ElasticApmTracer.java
@@ -26,6 +26,7 @@
import co.elastic.apm.agent.configuration.CoreConfiguration;
import co.elastic.apm.agent.configuration.ServiceNameUtil;
+import co.elastic.apm.agent.context.ExecutorServiceShutdownLifecycleListener;
import co.elastic.apm.agent.context.LifecycleListener;
import co.elastic.apm.agent.impl.error.ErrorCapture;
import co.elastic.apm.agent.impl.sampling.ProbabilitySampler;
@@ -59,6 +60,7 @@
import java.util.Deque;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
@@ -95,6 +97,7 @@ protected Deque> initialValue() {
private final CoreConfiguration coreConfiguration;
private final List activationListeners;
private final MetricRegistry metricRegistry;
+ private final ScheduledThreadPoolExecutor sharedPool;
private Sampler sampler;
boolean assertionsEnabled = false;
@@ -143,7 +146,8 @@ public void onChange(ConfigurationOption> configurationOption, Double oldValue
}
});
this.activationListeners = DependencyInjectingServiceLoader.load(ActivationListener.class, this);
- reporter.scheduleMetricReporting(metricRegistry, configurationRegistry.getConfig(ReporterConfiguration.class).getMetricsIntervalMs(), this);
+ sharedPool = ExecutorUtils.createSingleThreadSchedulingDaemonPool("shared");
+ lifecycleListeners.add(new ExecutorServiceShutdownLifecycleListener(sharedPool));
// sets the assertionsEnabled flag to true if indeed enabled
//noinspection AssertWithSideEffects
@@ -507,12 +511,12 @@ public synchronized void start(boolean premain) {
private boolean shouldDelayOnPremain() {
String javaVersion = System.getProperty("java.version");
return javaVersion != null &&
- javaVersion.startsWith("1.8") &&
+ (javaVersion.startsWith("1.7") || javaVersion.startsWith("1.8")) &&
ClassLoader.getSystemClassLoader().getResource("org/apache/catalina/startup/Bootstrap.class") != null;
}
private synchronized void startWithDelay(final long delayInitMs) {
- ThreadPoolExecutor pool = ExecutorUtils.createSingleThreadDeamonPool("tracer-initializer", 1);
+ ThreadPoolExecutor pool = ExecutorUtils.createSingleThreadDaemonPool("tracer-initializer", 1);
pool.submit(new Runnable() {
@Override
public void run() {
@@ -696,7 +700,7 @@ public void overrideServiceNameForClassLoader(@Nullable ClassLoader classLoader,
if (classLoader == null
|| serviceName == null || serviceName.isEmpty()
// if the service name is set explicitly, don't override it
- || !coreConfiguration.getServiceNameConfig().isDefault()) {
+ || coreConfiguration.getServiceNameConfig().getUsedKey() != null) {
return;
}
if (!serviceNameByClassLoader.containsKey(classLoader)) {
@@ -724,4 +728,7 @@ public MetaData getMetaData() {
return metaData;
}
+ public ScheduledThreadPoolExecutor getSharedSingleThreadedPool() {
+ return sharedPool;
+ }
}
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/ElasticApmTracerBuilder.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/ElasticApmTracerBuilder.java
index ee5dd923ad..b9b38ea127 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/ElasticApmTracerBuilder.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/ElasticApmTracerBuilder.java
@@ -170,7 +170,7 @@ private ElasticApmTracer build(boolean startTracer) {
}
private LifecycleListener scheduleReloadAtRate(final ConfigurationRegistry configurationRegistry, final int rate, TimeUnit seconds) {
- final ScheduledThreadPoolExecutor configurationReloader = ExecutorUtils.createSingleThreadSchedulingDeamonPool("configuration-reloader");
+ final ScheduledThreadPoolExecutor configurationReloader = ExecutorUtils.createSingleThreadSchedulingDaemonPool("configuration-reloader");
configurationReloader.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/GlobalTracer.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/GlobalTracer.java
index 0fadd7633f..2418c283c3 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/GlobalTracer.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/GlobalTracer.java
@@ -31,16 +31,12 @@
import co.elastic.apm.agent.impl.transaction.Span;
import co.elastic.apm.agent.impl.transaction.TextHeaderGetter;
import co.elastic.apm.agent.impl.transaction.Transaction;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.util.Objects;
public class GlobalTracer implements Tracer {
- private static final Logger logger = LoggerFactory.getLogger(GlobalTracer.class);
-
private static final GlobalTracer INSTANCE = new GlobalTracer();
private volatile Tracer tracer = NoopTracer.INSTANCE;
@@ -64,20 +60,25 @@ public static ElasticApmTracer requireTracerImpl() {
return Objects.requireNonNull(getTracerImpl(), "Registered tracer is not an instance of ElasticApmTracer");
}
- public static void setNoop() {
- set(NoopTracer.INSTANCE);
- }
-
- public static void set(Tracer tracer) {
+ public static synchronized void setNoop() {
TracerState currentTracerState = INSTANCE.tracer.getState();
if (currentTracerState != TracerState.UNINITIALIZED && currentTracerState != TracerState.STOPPED) {
- logger.warn("Overriding running tracer");
- // TODO throw exception, requires changes in tests
- // throw new IllegalStateException("Can't override tracer as current tracer is already running");
+ throw new IllegalStateException("Can't override tracer as current tracer is already running");
+ }
+ INSTANCE.tracer = NoopTracer.INSTANCE;
+ }
+
+ public static synchronized void init(Tracer tracer) {
+ if (!isNoop()) {
+ throw new IllegalStateException("Tracer is already initialized");
}
INSTANCE.tracer = tracer;
}
+ public static boolean isNoop() {
+ return INSTANCE.tracer == NoopTracer.INSTANCE;
+ }
+
@Nullable
public Transaction startRootTransaction(@Nullable ClassLoader initiatingClassLoader) {
return tracer.startRootTransaction(initiatingClassLoader);
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/circuitbreaker/CircuitBreaker.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/circuitbreaker/CircuitBreaker.java
index 2b5582d300..11bacc1da6 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/circuitbreaker/CircuitBreaker.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/circuitbreaker/CircuitBreaker.java
@@ -52,7 +52,7 @@ public CircuitBreaker(ElasticApmTracer tracer) {
this.tracer = tracer;
circuitBreakerConfiguration = tracer.getConfig(CircuitBreakerConfiguration.class);
pollInterval = circuitBreakerConfiguration.getStressMonitoringPollingIntervalMillis();
- threadPool = ExecutorUtils.createSingleThreadDeamonPool("circuit-breaker", 1);
+ threadPool = ExecutorUtils.createSingleThreadDaemonPool("circuit-breaker", 1);
}
@Override
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/context/Destination.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/context/Destination.java
index d8a88679db..8cc6b5fd48 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/context/Destination.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/context/Destination.java
@@ -143,7 +143,7 @@ public boolean hasContent() {
@Override
public void resetState() {
address.setLength(0);
- port = -1;
+ port = 0;
service.resetState();
}
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/context/web/WebConfiguration.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/context/web/WebConfiguration.java
index 90a37c3f04..0bff2a749b 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/context/web/WebConfiguration.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/context/web/WebConfiguration.java
@@ -11,9 +11,9 @@
* 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
@@ -58,7 +58,8 @@ public class WebConfiguration extends ConfigurationOptionProvider {
private final ConfigurationOption> ignoreUrls = ConfigurationOption
.builder(new ListValueConverter<>(new WildcardMatcherValueConverter()), List.class)
- .key("ignore_urls")
+ .key("transaction_ignore_urls")
+ .aliasKeys("ignore_urls")
.configurationCategory(HTTP_CATEGORY)
.description("Used to restrict requests to certain URLs from being instrumented.\n" +
"\n" +
@@ -107,7 +108,7 @@ public class WebConfiguration extends ConfigurationOptionProvider {
.configurationCategory(HTTP_CATEGORY)
.tags("experimental")
.description("If set to `true`,\n" +
- "transaction names of unsupported Servlet API-based frameworks will be in the form of `$method $path` instead of just `$method`.\n" +
+ "transaction names of unsupported Servlet API-based frameworks will be in the form of `$method $path` instead of just `$method unknown route`.\n" +
"\n" +
"WARNING: If your URLs contain path parameters like `/user/$userId`,\n" +
"you should be very careful when enabling this flag,\n" +
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/logging/LoggingConfiguration.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/logging/LoggingConfiguration.java
index 8035360a59..6ad4e5bb28 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/logging/LoggingConfiguration.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/logging/LoggingConfiguration.java
@@ -26,7 +26,9 @@
import co.elastic.apm.agent.configuration.converter.ByteValue;
import co.elastic.apm.agent.configuration.converter.ByteValueConverter;
+import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.config.Configurator;
+import org.apache.logging.log4j.status.StatusLogger;
import org.stagemonitor.configuration.ConfigurationOption;
import org.stagemonitor.configuration.ConfigurationOptionProvider;
import org.stagemonitor.configuration.source.ConfigurationSource;
@@ -65,6 +67,9 @@ public class LoggingConfiguration extends ConfigurationOptionProvider {
static final String SHIP_AGENT_LOGS = "ship_agent_logs";
static final String LOG_FORMAT_SOUT_KEY = "log_format_sout";
public static final String LOG_FORMAT_FILE_KEY = "log_format_file";
+ static final String INITIAL_LISTENERS_LEVEL = "log4j2.StatusLogger.level";
+ static final String INITIAL_STATUS_LOGGER_LEVEL = "org.apache.logging.log4j.simplelog.StatusLogger.level";
+ static final String DEFAULT_LISTENER_LEVEL = "Log4jDefaultStatusLevel";
/**
* We don't directly access most logging configuration values through the ConfigurationOption instance variables.
@@ -195,7 +200,34 @@ public void assertValid(Boolean value) {
.buildWithDefault(LogFormat.PLAIN_TEXT);
public static void init(List sources, String ephemeralId) {
- Configurator.initialize(new Log4j2ConfigurationFactory(sources, ephemeralId).getConfiguration());
+ // The initialization of log4j may produce errors if the traced application uses log4j settings (for
+ // example - through file in the classpath or System properties) that configures specific properties for
+ // loading classes by name. Since we shade our usage of log4j, such non-shaded classes may not (and should not)
+ // be found on the classpath.
+ // All handled Exceptions should not prevent us from using log4j further, as the system falls back to a default
+ // which we expect anyway. We take a calculated risk of ignoring such errors only through initialization time,
+ // assuming that errors that will make the logging system non-usable won't be handled.
+ String initialListenersLevel = System.setProperty(INITIAL_LISTENERS_LEVEL, "OFF");
+ String initialStatusLoggerLevel = System.setProperty(INITIAL_STATUS_LOGGER_LEVEL, "OFF");
+ String defaultListenerLevel = System.setProperty(DEFAULT_LISTENER_LEVEL, "OFF");
+ try {
+ Configurator.initialize(new Log4j2ConfigurationFactory(sources, ephemeralId).getConfiguration());
+ } catch (Throwable throwable) {
+ System.err.println("Failure during initialization of agent's log4j system: " + throwable.getMessage());
+ } finally {
+ restoreSystemProperty(INITIAL_LISTENERS_LEVEL, initialListenersLevel);
+ restoreSystemProperty(INITIAL_STATUS_LOGGER_LEVEL, initialStatusLoggerLevel);
+ restoreSystemProperty(DEFAULT_LISTENER_LEVEL, defaultListenerLevel);
+ StatusLogger.getLogger().setLevel(Level.ERROR);
+ }
+ }
+
+ private static void restoreSystemProperty(String key, @Nullable String originalValue) {
+ if (originalValue != null) {
+ System.setProperty(key, originalValue);
+ } else {
+ System.clearProperty(key);
+ }
}
public String getLogFile() {
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/metrics/MetricRegistry.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/metrics/MetricRegistry.java
index 78799682d3..651cbd30b9 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/metrics/MetricRegistry.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/metrics/MetricRegistry.java
@@ -11,9 +11,9 @@
* 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
@@ -159,14 +159,30 @@ public DoubleSupplier getGauge(String name, Labels labels) {
return null;
}
- public void report(MetricsReporter metricsReporter) {
+ /**
+ * Executes the following steps within a single read-operation critical section:
+ *
+ *
Switch between active and inactive MetricSet containers
+ *
Report the inactivated MetricSets (optional)
+ *
Reset the inactivated MetricSets
+ *
+ *
+ * @param metricsReporter a reporter to be used for reporting the inactivated MetricSets. May be {@code null}
+ * if reporting is not required.
+ */
+ public void flipPhaseAndReport(@Nullable MetricsReporter metricsReporter) {
try {
phaser.readerLock();
ConcurrentMap temp = inactiveMetricSets;
inactiveMetricSets = activeMetricSets;
activeMetricSets = temp;
phaser.flipPhase();
- metricsReporter.report(inactiveMetricSets);
+ if (metricsReporter != null) {
+ metricsReporter.report(inactiveMetricSets);
+ }
+ for (MetricSet metricSet : inactiveMetricSets.values()) {
+ metricSet.resetState();
+ }
} finally {
phaser.readerUnlock();
}
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/metrics/MetricSet.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/metrics/MetricSet.java
index 30eadad591..f15fba97a9 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/metrics/MetricSet.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/metrics/MetricSet.java
@@ -11,9 +11,9 @@
* 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
@@ -24,6 +24,8 @@
*/
package co.elastic.apm.agent.metrics;
+import co.elastic.apm.agent.objectpool.Recyclable;
+
import javax.annotation.Nullable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -44,7 +46,7 @@
* {"metricset":{"timestamp":1545047730692000,"tags":{"name":"G1 Old Generation"}, "samples":{"jvm.gc.time":{"value":0.0},"jvm.gc.count":{"value":0.0}}}}
*
*/
-public class MetricSet {
+public class MetricSet implements Recyclable {
private final Labels.Immutable labels;
private final ConcurrentMap gauges;
// low load factor as hash collisions are quite costly when tracking breakdown metrics
@@ -107,7 +109,16 @@ public boolean hasContent() {
return !gauges.isEmpty() || hasNonEmptyTimer || hasNonEmptyCounter;
}
- public void onAfterReport() {
+ /**
+ * Should be called only when the MetricSet is inactive
+ */
+ public void resetState() {
+ for (Timer timer : timers.values()) {
+ timer.resetState();
+ }
+ for (AtomicLong counter : counters.values()) {
+ counter.set(0);
+ }
hasNonEmptyTimer = false;
hasNonEmptyCounter = false;
}
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/metrics/builtin/CGroupMetrics.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/metrics/builtin/CGroupMetrics.java
new file mode 100644
index 0000000000..3f410503eb
--- /dev/null
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/metrics/builtin/CGroupMetrics.java
@@ -0,0 +1,308 @@
+/*-
+ * #%L
+ * Elastic APM Java agent
+ * %%
+ * Copyright (C) 2018 - 2020 Elastic and contributors
+ * %%
+ * 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.
+ * #L%
+ */
+package co.elastic.apm.agent.metrics.builtin;
+
+import co.elastic.apm.agent.context.AbstractLifecycleListener;
+import co.elastic.apm.agent.impl.ElasticApmTracer;
+import co.elastic.apm.agent.metrics.DoubleSupplier;
+import co.elastic.apm.agent.metrics.Labels;
+import co.elastic.apm.agent.metrics.MetricRegistry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.stagemonitor.util.StringUtils;
+
+import javax.annotation.Nullable;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Record metrics related to the CGroup Usage.
+ *
+ * Implements the cgroup metrics spec - https://github.com/elastic/apm/blob/master/docs/agents/agent-development.md#cgroup-metrics
+ */
+public class CGroupMetrics extends AbstractLifecycleListener {
+
+ private static final String PROC_SELF_CGROUP = "/proc/self/cgroup";
+ private static final String PROC_SELF_MOUNTINFO = "/proc/self/mountinfo";
+ private static final String DEFAULT_SYS_FS_CGROUP = "/sys/fs/cgroup";
+
+ private static final String CGROUP1_MAX_MEMORY = "memory.limit_in_bytes";
+ private static final String CGROUP1_USED_MEMORY = "memory.usage_in_bytes";
+ private static final String CGROUP2_MAX_MEMORY = "memory.max";
+ private static final String CGROUP2_USED_MEMORY = "memory.current";
+ private static final String CGROUP_MEMORY_STAT = "memory.stat";
+ private static final String CGROUP1_UNLIMITED = "9223372036854771712";
+ private static final String CGROUP2_UNLIMITED = "max";
+
+ static final Pattern MEMORY_CGROUP = Pattern.compile("^\\d+:memory:.*");
+ static final Pattern CGROUP1_MOUNT_POINT = Pattern.compile("^\\d+? \\d+? .+? .+? (.*?) .*cgroup.*memory.*");
+ static final Pattern CGROUP2_MOUNT_POINT = Pattern.compile("^\\d+? \\d+? .+? .+? (.*?) .*cgroup2.*cgroup.*");
+
+ private static final Logger logger = LoggerFactory.getLogger(CGroupMetrics.class);
+
+
+ @Nullable
+ private final CgroupFiles cgroupFiles;
+
+ public CGroupMetrics() {
+ this(new File(PROC_SELF_CGROUP), new File(PROC_SELF_MOUNTINFO));
+ }
+
+ CGroupMetrics(File procSelfCgroup, File mountInfo) {
+ cgroupFiles = findCgroupFiles(procSelfCgroup, mountInfo);
+ }
+
+ /**
+ * Finds cgroup files (if any)
+ *
+ * @param procSelfCgroup /proc/self/cgroup file
+ * @param mountInfo /proc/self/mountinfo file
+ * @return a holder for the memory cgroup files if found or {@code null} if not found
+ */
+ @Nullable
+ private CgroupFiles findCgroupFiles(File procSelfCgroup, File mountInfo) {
+ if (!procSelfCgroup.canRead()) {
+ logger.debug("Cannot find/read /proc/self/cgroup file. Cgroup metrics will not be reported.");
+ return null;
+ }
+
+ String cgroupLine = null;
+ try (BufferedReader fileReader = new BufferedReader(new FileReader(procSelfCgroup))) {
+ String currentLine = fileReader.readLine();
+ while (currentLine != null) {
+ if (cgroupLine == null && currentLine.startsWith("0:")) {
+ cgroupLine = currentLine;
+ }
+ if (MEMORY_CGROUP.matcher(currentLine).matches()) {
+ cgroupLine = currentLine;
+ break;
+ }
+ currentLine = fileReader.readLine();
+ }
+
+ if (cgroupLine == null) {
+ logger.warn("No /proc/self/cgroup file line matched the tested patterns. Cgroup metrics will not be reported.");
+ return null;
+ }
+
+ CgroupFiles cgroupFiles;
+
+ // Try to discover the cgroup fs path from the mountinfo file
+ if (mountInfo.canRead()) {
+ String mountLine = null;
+ try (BufferedReader fileMountInfoReader = new BufferedReader(new FileReader(mountInfo))) {
+ mountLine = fileMountInfoReader.readLine();
+ while (mountLine != null) {
+ // cgroup v2
+ String rootCgroupFsPath = applyCgroupRegex(CGROUP2_MOUNT_POINT, mountLine);
+ if (rootCgroupFsPath != null) {
+ cgroupFiles = createCgroup2Files(cgroupLine, new File(rootCgroupFsPath));
+ if (cgroupFiles != null) {
+ return cgroupFiles;
+ }
+ }
+
+ // cgroup v1
+ String memoryMountPath = applyCgroupRegex(CGROUP1_MOUNT_POINT, mountLine);
+ if (memoryMountPath != null) {
+ cgroupFiles = createCgroup1Files(new File(memoryMountPath));
+ if (cgroupFiles != null) {
+ return cgroupFiles;
+ }
+ }
+
+ mountLine = fileMountInfoReader.readLine();
+ }
+ } catch (Exception e) {
+ logger.info("Failed to discover memory mount files path based on mountinfo line '{}'.", mountLine);
+ }
+ } else {
+ logger.info("Failed to find/read /proc/self/mountinfo file. Looking for memory files in /sys/fs/cgroup.");
+ }
+
+ // Failed to auto-discover the cgroup fs path from mountinfo, fall back to /sys/fs/cgroup
+ // cgroup v2
+ cgroupFiles = createCgroup2Files(cgroupLine, new File(DEFAULT_SYS_FS_CGROUP));
+ if (cgroupFiles != null) {
+ return cgroupFiles;
+ }
+ // cgroup v1
+ cgroupFiles = createCgroup1Files(new File(DEFAULT_SYS_FS_CGROUP + File.pathSeparator + "memory"));
+ if (cgroupFiles != null) {
+ return cgroupFiles;
+ }
+
+ } catch (Exception e) {
+ logger.error("Failed to discover memory mount files path based on cgroup line '" + cgroupLine +
+ "'. Cgroup metrics will not be reported", e);
+ }
+
+ return null;
+ }
+
+ @Nullable
+ String applyCgroupRegex(Pattern regex, String mountLine) {
+ Matcher matcher = regex.matcher(mountLine);
+ if (matcher.matches()) {
+ return matcher.group(1);
+ }
+ return null;
+ }
+
+ @Nullable
+ private CgroupFiles createCgroup2Files(String cgroupLine, File rootCgroupFsPath) throws IOException {
+ final String[] cgroupLineParts = StringUtils.split(cgroupLine, ':');
+ String sliceSubdir = cgroupLineParts[cgroupLineParts.length - 1];
+ File maxMemoryFile = new File(rootCgroupFsPath, sliceSubdir + File.separatorChar + CGROUP2_MAX_MEMORY);
+ if (maxMemoryFile.canRead()) {
+ maxMemoryFile = getMaxMemoryFile(maxMemoryFile, CGROUP2_UNLIMITED);
+ return new CgroupFiles(
+ maxMemoryFile,
+ new File(rootCgroupFsPath, sliceSubdir + File.separator + CGROUP2_USED_MEMORY),
+ new File(rootCgroupFsPath, sliceSubdir + File.separator + CGROUP_MEMORY_STAT)
+ );
+ }
+ return null;
+ }
+
+ @Nullable
+ private CgroupFiles createCgroup1Files(File memoryMountPath) throws IOException {
+ File maxMemoryFile = new File(memoryMountPath, CGroupMetrics.CGROUP1_MAX_MEMORY);
+ if (maxMemoryFile.canRead()) {
+ maxMemoryFile = getMaxMemoryFile(maxMemoryFile, CGROUP1_UNLIMITED);
+ return new CgroupFiles(
+ maxMemoryFile,
+ new File(memoryMountPath, CGroupMetrics.CGROUP1_USED_MEMORY),
+ new File(memoryMountPath, CGroupMetrics.CGROUP_MEMORY_STAT)
+ );
+ }
+ return null;
+ }
+
+ @Nullable
+ private File getMaxMemoryFile(File maxMemoryFile, String cgroupUnlimitedConstant) throws IOException {
+ try(BufferedReader maxFileReader = new BufferedReader(new FileReader(maxMemoryFile))) {
+ String memMaxLine = maxFileReader.readLine();
+ if (cgroupUnlimitedConstant.equalsIgnoreCase(memMaxLine)) {
+ // Make sure we don't send the max metric when cgroup is not bound to a memory limit
+ maxMemoryFile = null;
+ }
+ }
+ return maxMemoryFile;
+ }
+
+ @Override
+ public void start(ElasticApmTracer tracer) {
+ bindTo(tracer.getMetricRegistry());
+ }
+
+ void bindTo(MetricRegistry metricRegistry) {
+ if (cgroupFiles != null) {
+ metricRegistry.addUnlessNan("system.process.cgroup.memory.stats.inactive_file.bytes", Labels.EMPTY, new DoubleSupplier() {
+ @Override
+ public double get() {
+ try (BufferedReader fileReaderStatFile = new BufferedReader(new FileReader(cgroupFiles.getStatMemoryFile()))) {
+ String statLine = fileReaderStatFile.readLine();
+ String inactiveBytes = null;
+ while (statLine != null) {
+ final String[] statLineSplit = StringUtils.split(statLine, ' ');
+ if (statLineSplit.length > 1) {
+ if ("total_inactive_file".equals(statLineSplit[0])) {
+ inactiveBytes = statLineSplit[1];
+ break;
+ } else if ("inactive_file".equals(statLineSplit[0])) {
+ inactiveBytes = statLineSplit[1];
+ }
+ }
+ statLine = fileReaderStatFile.readLine();
+ }
+ return inactiveBytes != null ? Long.parseLong(inactiveBytes) : Double.NaN;
+ } catch (Exception e) {
+ logger.debug("Failed to read " + cgroupFiles.getStatMemoryFile().getAbsolutePath() + " file", e);
+ return Double.NaN;
+ }
+ }
+ });
+
+ metricRegistry.addUnlessNan("system.process.cgroup.memory.mem.usage.bytes", Labels.EMPTY, new DoubleSupplier() {
+ @Override
+ public double get() {
+ try (BufferedReader fileReaderMemoryUsed = new BufferedReader(new FileReader(cgroupFiles.getUsedMemoryFile()))) {
+ return Long.parseLong(fileReaderMemoryUsed.readLine());
+ } catch (Exception e) {
+ logger.debug("Failed to read " + cgroupFiles.getUsedMemoryFile().getAbsolutePath() + " file", e);
+ return Double.NaN;
+ }
+ }
+ });
+
+ final File maxMemoryFile = cgroupFiles.getMaxMemoryFile();
+ if (maxMemoryFile != null) {
+ metricRegistry.addUnlessNan("system.process.cgroup.memory.mem.limit.bytes", Labels.EMPTY, new DoubleSupplier() {
+ @Override
+ public double get() {
+ try (BufferedReader fileReaderMemoryMax = new BufferedReader(new FileReader(maxMemoryFile))) {
+ return Long.parseLong(fileReaderMemoryMax.readLine());
+ } catch (Exception e) {
+ logger.debug("Failed to read " + maxMemoryFile + " file", e);
+ return Double.NaN;
+ }
+ }
+ });
+ }
+ }
+ }
+
+ private static class CgroupFiles {
+
+ @Nullable // may be null if memory mount is found for the cgroup, but memory is unlimited
+ private final File maxMemoryFile;
+ private final File usedMemoryFile;
+ private final File statMemoryFile;
+
+ public CgroupFiles(@Nullable File maxMemoryFile, File usedMemoryFile, File statMemoryFile) {
+ this.maxMemoryFile = maxMemoryFile;
+ this.usedMemoryFile = usedMemoryFile;
+ this.statMemoryFile = statMemoryFile;
+ }
+
+ @Nullable
+ public File getMaxMemoryFile() {
+ return maxMemoryFile;
+ }
+
+ public File getUsedMemoryFile() {
+ return usedMemoryFile;
+ }
+
+ public File getStatMemoryFile() {
+ return statMemoryFile;
+ }
+ }
+}
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/report/AbstractIntakeApiHandler.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/AbstractIntakeApiHandler.java
index 1f67567f98..92d81918b5 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/report/AbstractIntakeApiHandler.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/AbstractIntakeApiHandler.java
@@ -32,11 +32,14 @@
import org.stagemonitor.util.IOUtils;
import javax.annotation.Nullable;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLSocketFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
@@ -99,20 +102,49 @@ protected boolean shouldEndRequest() {
return endRequest;
}
+ @Nullable
protected HttpURLConnection startRequest(String endpoint) throws IOException {
final HttpURLConnection connection = apmServerClient.startRequest(endpoint);
- if (logger.isDebugEnabled()) {
- logger.debug("Starting new request to {}", connection.getURL());
+ if (connection != null) {
+ try {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Starting new request to {}", connection.getURL());
+ }
+ connection.setRequestMethod("POST");
+ connection.setDoOutput(true);
+ connection.setChunkedStreamingMode(DslJsonSerializer.BUFFER_SIZE);
+ connection.setRequestProperty("Content-Encoding", "deflate");
+ connection.setRequestProperty("Content-Type", "application/x-ndjson");
+ connection.setUseCaches(false);
+ connection.connect();
+ os = new DeflaterOutputStream(connection.getOutputStream(), deflater);
+ os.write(metaData);
+ } catch (IOException e) {
+ logger.error("Error trying to connect to APM Server. Some details about SSL configurations corresponding " +
+ "the current connection are logged at INFO level.");
+ if (logger.isInfoEnabled() && connection instanceof HttpsURLConnection) {
+ HttpsURLConnection httpsURLConnection = (HttpsURLConnection) connection;
+ try {
+ logger.info("Cipher suite used for this connection: {}", httpsURLConnection.getCipherSuite());
+ } catch (Exception e1) {
+ SSLSocketFactory sslSocketFactory = httpsURLConnection.getSSLSocketFactory();
+ logger.info("Default cipher suites: {}", Arrays.toString(sslSocketFactory.getDefaultCipherSuites()));
+ logger.info("Supported cipher suites: {}", Arrays.toString(sslSocketFactory.getSupportedCipherSuites()));
+ }
+ try {
+ logger.info("APM Server certificates: {}", Arrays.toString(httpsURLConnection.getServerCertificates()));
+ } catch (Exception e1) {
+ // ignore - invalid
+ }
+ try {
+ logger.info("Local certificates: {}", Arrays.toString(httpsURLConnection.getLocalCertificates()));
+ } catch (Exception e1) {
+ // ignore - invalid
+ }
+ }
+ throw e;
+ }
}
- connection.setRequestMethod("POST");
- connection.setDoOutput(true);
- connection.setChunkedStreamingMode(DslJsonSerializer.BUFFER_SIZE);
- connection.setRequestProperty("Content-Encoding", "deflate");
- connection.setRequestProperty("Content-Type", "application/x-ndjson");
- connection.setUseCaches(false);
- connection.connect();
- os = new DeflaterOutputStream(connection.getOutputStream(), deflater);
- os.write(metaData);
return connection;
}
@@ -142,6 +174,7 @@ public void endRequest() {
} finally {
HttpUtils.consumeAndClose(connection);
connection = null;
+ os = null;
deflater.reset();
currentlyTransmitting = 0;
}
@@ -199,6 +232,10 @@ public long getDropped() {
return dropped;
}
+ public int getErrorCount() {
+ return errorCount;
+ }
+
public void close() {
shutDown = true;
synchronized (WAIT_LOCK) {
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ApmServerClient.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ApmServerClient.java
index 3892de3866..c804bd61fb 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ApmServerClient.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ApmServerClient.java
@@ -46,7 +46,10 @@
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -115,8 +118,13 @@ private static List shuffleUrls(List serverUrls) {
return copy;
}
+ @Nullable
HttpURLConnection startRequest(String relativePath) throws IOException {
- return startRequestToUrl(appendPathToCurrentUrl(relativePath));
+ URL url = appendPathToCurrentUrl(relativePath);
+ if (url == null) {
+ return null;
+ }
+ return startRequestToUrl(url);
}
@Nonnull
@@ -166,9 +174,13 @@ private URLConnection openUrlConnectionThreadSafely(URL url) throws IOException
}
}
- @Nonnull
+ @Nullable
URL appendPathToCurrentUrl(String apmServerPath) throws MalformedURLException {
- return appendPath(getCurrentUrl(), apmServerPath);
+ URL currentUrl = getCurrentUrl();
+ if (currentUrl == null) {
+ return null;
+ }
+ return appendPath(currentUrl, apmServerPath);
}
@Nonnull
@@ -274,8 +286,12 @@ public List executeForAllUrls(String path, ConnectionHandler connectio
return results;
}
+ @Nullable
URL getCurrentUrl() {
List serverUrls = getServerUrls();
+ if (serverUrls.isEmpty()) {
+ return null;
+ }
return serverUrls.get(errorCount.get() % serverUrls.size());
}
@@ -313,6 +329,14 @@ public boolean supportsLogsEndpoint() {
return isAtLeast(VERSION_7_9);
}
+ @Nullable
+ Version getApmServerVersion(long timeout, TimeUnit timeUnit) throws InterruptedException, ExecutionException, TimeoutException {
+ if (apmServerVersion != null) {
+ return apmServerVersion.get(timeout, timeUnit);
+ }
+ return null;
+ }
+
public boolean isAtLeast(Version apmServerVersion) {
if (this.apmServerVersion == null) {
throw new IllegalStateException("Called before init event");
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ApmServerHealthChecker.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ApmServerHealthChecker.java
index c756329bf2..10601d7325 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ApmServerHealthChecker.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ApmServerHealthChecker.java
@@ -55,7 +55,7 @@ public ApmServerHealthChecker(ApmServerClient apmServerClient) {
}
public Future checkHealthAndGetMinVersion() {
- ThreadPoolExecutor pool = ExecutorUtils.createSingleThreadDeamonPool("server-healthcheck", 1);
+ ThreadPoolExecutor pool = ExecutorUtils.createSingleThreadDaemonPool("server-healthcheck", 1);
try {
return pool.submit(this);
} finally {
@@ -119,6 +119,6 @@ public Version withConnection(HttpURLConnection connection) {
if (!versions.isEmpty()) {
return Collections.min(versions);
}
- return null;
+ return Version.UNKNOWN_VERSION;
}
}
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ApmServerReporter.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ApmServerReporter.java
index 330254026c..fd66744b2b 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ApmServerReporter.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ApmServerReporter.java
@@ -24,15 +24,13 @@
*/
package co.elastic.apm.agent.report;
-import co.elastic.apm.agent.impl.ElasticApmTracer;
import co.elastic.apm.agent.impl.error.ErrorCapture;
import co.elastic.apm.agent.impl.transaction.Span;
import co.elastic.apm.agent.impl.transaction.Transaction;
-import co.elastic.apm.agent.metrics.MetricRegistry;
import co.elastic.apm.agent.report.disruptor.ExponentionallyIncreasingSleepingWaitStrategy;
-import co.elastic.apm.agent.util.ExecutorUtils;
import co.elastic.apm.agent.util.MathUtils;
import co.elastic.apm.agent.util.ThreadUtils;
+import com.dslplatform.json.JsonWriter;
import com.lmax.disruptor.EventFactory;
import com.lmax.disruptor.EventTranslator;
import com.lmax.disruptor.EventTranslatorOneArg;
@@ -42,9 +40,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import javax.annotation.Nullable;
import java.util.concurrent.Future;
-import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@@ -84,6 +80,12 @@ public void translateTo(ReportingEvent event, long sequence, ErrorCapture error)
event.setError(error);
}
};
+ private static final EventTranslatorOneArg JSON_WRITER_EVENT_TRANSLATOR = new EventTranslatorOneArg() {
+ @Override
+ public void translateTo(ReportingEvent event, long sequence, JsonWriter jsonWriter) {
+ event.setJsonWriter(jsonWriter);
+ }
+ };
private static final EventTranslator SHUTDOWN_EVENT_TRANSLATOR = new EventTranslator() {
@Override
public void translateTo(ReportingEvent event, long sequence) {
@@ -96,8 +98,6 @@ public void translateTo(ReportingEvent event, long sequence) {
private final boolean dropTransactionIfQueueFull;
private final ReportingEventHandler reportingEventHandler;
private final boolean syncReport;
- @Nullable
- private ScheduledThreadPoolExecutor metricsReportingScheduler;
public ApmServerReporter(boolean dropTransactionIfQueueFull, ReporterConfiguration reporterConfiguration,
ReportingEventHandler reportingEventHandler) {
@@ -238,9 +238,6 @@ public void close() {
logger.warn("Timeout while shutting down disruptor");
}
reportingEventHandler.close();
- if (metricsReportingScheduler != null) {
- metricsReportingScheduler.shutdown();
- }
}
@Override
@@ -254,23 +251,13 @@ public void report(ErrorCapture error) {
}
@Override
- public void scheduleMetricReporting(final MetricRegistry metricRegistry, long intervalMs, final ElasticApmTracer tracer) {
- if (intervalMs > 0 && metricsReportingScheduler == null) {
- metricsReportingScheduler = ExecutorUtils.createSingleThreadSchedulingDeamonPool("metrics-reporter");
- metricsReportingScheduler.scheduleAtFixedRate(new Runnable() {
- @Override
- public void run() {
- if (!tracer.isRunning()) {
- return;
- }
- disruptor.getRingBuffer().tryPublishEvent(new EventTranslatorOneArg() {
- @Override
- public void translateTo(ReportingEvent event, long sequence, MetricRegistry metricRegistry) {
- event.reportMetrics(metricRegistry);
- }
- }, metricRegistry);
- }
- }, intervalMs, intervalMs, TimeUnit.MILLISECONDS);
+ public void report(JsonWriter jsonWriter) {
+ if (jsonWriter.size() == 0) {
+ return;
+ }
+ tryAddEventToRingBuffer(jsonWriter, JSON_WRITER_EVENT_TRANSLATOR);
+ if (syncReport) {
+ waitForFlush();
}
}
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/report/IntakeV2ReportingEventHandler.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/IntakeV2ReportingEventHandler.java
index e77666fa28..2940a3fc14 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/report/IntakeV2ReportingEventHandler.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/IntakeV2ReportingEventHandler.java
@@ -94,13 +94,23 @@ private void handleEvent(ReportingEvent event, long sequence, boolean endOfBatch
if (connection == null) {
connection = startRequest(INTAKE_V2_URL);
}
- writeEvent(event);
+ if (connection != null) {
+ writeEvent(event);
+ } else {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Failed to get APM server connection, dropping event: {}", event);
+ }
+ dropped++;
+ }
} catch (Exception e) {
logger.error("Failed to handle event of type {} with this error: {}", event.getType(), e.getMessage());
logger.debug("Event handling failure", e);
endRequest();
onConnectionError(null, currentlyTransmitting + 1, 0);
+ } finally {
+ event.end();
}
+
if (shouldEndRequest()) {
endRequest();
}
@@ -119,17 +129,14 @@ private void writeEvent(ReportingEvent event) {
if (event.getTransaction() != null) {
currentlyTransmitting++;
payloadSerializer.serializeTransactionNdJson(event.getTransaction());
- event.getTransaction().decrementReferences();
} else if (event.getSpan() != null) {
currentlyTransmitting++;
payloadSerializer.serializeSpanNdJson(event.getSpan());
- event.getSpan().decrementReferences();
} else if (event.getError() != null) {
currentlyTransmitting++;
payloadSerializer.serializeErrorNdJson(event.getError());
- event.getError().recycle();
- } else if (event.getMetricRegistry() != null) {
- payloadSerializer.serializeMetrics(event.getMetricRegistry());
+ } else if (event.getJsonWriter() != null) {
+ payloadSerializer.writeBytes(event.getJsonWriter().getByteBuffer(), event.getJsonWriter().size());
}
}
@@ -141,17 +148,20 @@ private void cancelTimeout() {
}
@Override
+ @Nullable
protected HttpURLConnection startRequest(String endpoint) throws IOException {
HttpURLConnection connection = super.startRequest(endpoint);
- if (os != null) {
- payloadSerializer.setOutputStream(os);
- }
- if (reporter != null) {
- timeoutTask = new IntakeV2ReportingEventHandler.FlushOnTimeoutTimerTask(reporter);
- if (logger.isDebugEnabled()) {
- logger.debug("Scheduling request timeout in {}", reporterConfiguration.getApiRequestTime());
+ if (connection != null) {
+ if (os != null) {
+ payloadSerializer.setOutputStream(os);
+ }
+ if (reporter != null) {
+ timeoutTask = new FlushOnTimeoutTimerTask(reporter);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Scheduling request timeout in {}", reporterConfiguration.getApiRequestTime());
+ }
+ timeoutTimer.schedule(timeoutTask, reporterConfiguration.getApiRequestTime().getMillis());
}
- timeoutTimer.schedule(timeoutTask, reporterConfiguration.getApiRequestTime().getMillis());
}
return connection;
}
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/report/Reporter.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/Reporter.java
index 1bc85e0b80..e4c2d332b3 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/report/Reporter.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/Reporter.java
@@ -24,11 +24,10 @@
*/
package co.elastic.apm.agent.report;
-import co.elastic.apm.agent.impl.ElasticApmTracer;
import co.elastic.apm.agent.impl.error.ErrorCapture;
import co.elastic.apm.agent.impl.transaction.Span;
import co.elastic.apm.agent.impl.transaction.Transaction;
-import co.elastic.apm.agent.metrics.MetricRegistry;
+import com.dslplatform.json.JsonWriter;
import java.io.Closeable;
import java.util.concurrent.Future;
@@ -43,6 +42,8 @@ public interface Reporter extends Closeable {
void report(ErrorCapture error);
+ void report(JsonWriter jsonWriter);
+
long getDropped();
long getReported();
@@ -51,6 +52,4 @@ public interface Reporter extends Closeable {
@Override
void close();
-
- void scheduleMetricReporting(MetricRegistry metricRegistry, long intervalMs, final ElasticApmTracer tracer);
}
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ReporterConfiguration.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ReporterConfiguration.java
index 338221caf4..f89a6ebccc 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ReporterConfiguration.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ReporterConfiguration.java
@@ -67,7 +67,7 @@ public class ReporterConfiguration extends ConfigurationOptionProvider {
.sensitive()
.build();
- private final ConfigurationOption> serverUrl = ConfigurationOption.urlsOption()
+ private final ConfigurationOption> serverUrls = ConfigurationOption.urlsOption()
.key("server_urls")
.aliasKeys("server_url")
.configurationCategory(REPORTER_CATEGORY)
@@ -78,9 +78,16 @@ public class ReporterConfiguration extends ConfigurationOptionProvider {
"Achieves load-balancing by shuffling the list of configured URLs.\n" +
"When multiple agents are active, they'll tend towards spreading evenly across the set of servers due to randomization.\n" +
"\n" +
- "If outgoing HTTP traffic has to go through a proxy," +
+ "If set to an empty string, the agent will work as usual, except from any task requiring communication with \n" +
+ "the APM server (since 1.18.0). Events will be dropped as long as no valid server URLs are set. \n" +
+ "\n" +
+ "If SSL is enabled on the APM Server, use the `https` protocol. For more information, see \n" +
+ "<>.\n" +
+ "\n" +
+ "If outgoing HTTP traffic has to go through a proxy,\n" +
"you can use the Java system properties `http.proxyHost` and `http.proxyPort` to set that up.\n" +
- "See also [Java's proxy documentation](https://docs.oracle.com/javase/8/docs/technotes/guides/net/proxies.html) for more information.\n" +
+ "See also https://docs.oracle.com/javase/8/docs/technotes/guides/net/proxies.html[Java's proxy documentation] \n" +
+ "for more information.\n" +
"\n" +
"NOTE: This configuration can only be reloaded dynamically as of 1.8.0")
.dynamic(true)
@@ -192,7 +199,7 @@ public String getApiKey() {
}
public List getServerUrls() {
- return serverUrl.get();
+ return serverUrls.get();
}
public TimeDuration getServerTimeout() {
@@ -232,7 +239,7 @@ public List getDisableMetrics() {
}
public ConfigurationOption> getServerUrlsOption() {
- return this.serverUrl;
+ return this.serverUrls;
}
}
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ReportingEvent.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ReportingEvent.java
index 07f90e95f5..4301e64e0f 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ReportingEvent.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/ReportingEvent.java
@@ -11,9 +11,9 @@
* 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
@@ -27,13 +27,13 @@
import co.elastic.apm.agent.impl.error.ErrorCapture;
import co.elastic.apm.agent.impl.transaction.Span;
import co.elastic.apm.agent.impl.transaction.Transaction;
-import co.elastic.apm.agent.metrics.MetricRegistry;
+import com.dslplatform.json.JsonWriter;
import javax.annotation.Nullable;
import static co.elastic.apm.agent.report.ReportingEvent.ReportingEventType.ERROR;
import static co.elastic.apm.agent.report.ReportingEvent.ReportingEventType.FLUSH;
-import static co.elastic.apm.agent.report.ReportingEvent.ReportingEventType.METRICS;
+import static co.elastic.apm.agent.report.ReportingEvent.ReportingEventType.JSON_WRITER;
import static co.elastic.apm.agent.report.ReportingEvent.ReportingEventType.SHUTDOWN;
import static co.elastic.apm.agent.report.ReportingEvent.ReportingEventType.SPAN;
import static co.elastic.apm.agent.report.ReportingEvent.ReportingEventType.TRANSACTION;
@@ -48,14 +48,14 @@ public class ReportingEvent {
@Nullable
private Span span;
@Nullable
- private MetricRegistry metricRegistry;
+ private JsonWriter jsonWriter;
public void resetState() {
this.transaction = null;
this.type = null;
this.error = null;
this.span = null;
- this.metricRegistry = null;
+ this.jsonWriter = null;
}
@Nullable
@@ -97,21 +97,43 @@ public void setSpan(Span span) {
this.type = SPAN;
}
- public void reportMetrics(MetricRegistry metricRegistry) {
- this.metricRegistry = metricRegistry;
- this.type = METRICS;
- }
-
public void shutdownEvent() {
this.type = SHUTDOWN;
}
+ @Override
+ public String toString() {
+ StringBuilder description = new StringBuilder();
+ description.append("Type: ").append(type);
+ if (transaction != null) {
+ description.append(", ").append(transaction.toString());
+ } else if (span != null) {
+ description.append(", ").append(span.toString());
+ }
+ return description.toString();
+ }
+
@Nullable
- public MetricRegistry getMetricRegistry() {
- return metricRegistry;
+ public JsonWriter getJsonWriter() {
+ return jsonWriter;
+ }
+
+ public void setJsonWriter(@Nullable JsonWriter jsonWriter) {
+ this.jsonWriter = jsonWriter;
+ this.type = JSON_WRITER;
+ }
+
+ public void end() {
+ if (transaction != null) {
+ transaction.decrementReferences();
+ } else if (span != null) {
+ span.decrementReferences();
+ } else if (error != null) {
+ error.recycle();
+ }
}
enum ReportingEventType {
- FLUSH, TRANSACTION, SPAN, ERROR, METRICS, SHUTDOWN
+ FLUSH, TRANSACTION, SPAN, ERROR, SHUTDOWN, JSON_WRITER
}
}
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/report/serialize/DslJsonSerializer.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/serialize/DslJsonSerializer.java
index 1026d65930..57c5267cd7 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/report/serialize/DslJsonSerializer.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/serialize/DslJsonSerializer.java
@@ -55,8 +55,6 @@
import co.elastic.apm.agent.impl.transaction.TraceContext;
import co.elastic.apm.agent.impl.transaction.Transaction;
import co.elastic.apm.agent.metrics.Labels;
-import co.elastic.apm.agent.metrics.MetricRegistry;
-import co.elastic.apm.agent.metrics.MetricSet;
import co.elastic.apm.agent.report.ApmServerClient;
import co.elastic.apm.agent.util.HexUtils;
import co.elastic.apm.agent.util.PotentiallyMultiValuedMap;
@@ -89,7 +87,7 @@
import static com.dslplatform.json.JsonWriter.OBJECT_START;
import static com.dslplatform.json.JsonWriter.QUOTE;
-public class DslJsonSerializer implements PayloadSerializer, MetricRegistry.MetricsReporter {
+public class DslJsonSerializer implements PayloadSerializer {
/**
* Matches default ZLIB buffer size.
@@ -223,16 +221,6 @@ public int getBufferSize() {
return jw.size();
}
- @Override
- public void report(Map extends Labels, MetricSet> metricSets) {
- MetricRegistrySerializer.serialize(metricSets, replaceBuilder, jw);
- }
-
- @Override
- public void serializeMetrics(MetricRegistry metricRegistry) {
- metricRegistry.report(this);
- }
-
@Override
public void serializeFileMetaData(File file) {
jw.writeByte(JsonWriter.OBJECT_START);
@@ -256,6 +244,11 @@ public JsonWriter getJsonWriter() {
return jw;
}
+ @Override
+ public void writeBytes(byte[] bytes, int len) {
+ jw.writeAscii(bytes, len);
+ }
+
private void serializeErrors(List errors) {
writeFieldName("errors");
jw.writeByte(ARRAY_START);
@@ -1248,7 +1241,7 @@ void writeLastField(final String fieldName, @Nullable final CharSequence value)
writeLastField(fieldName, value, replaceBuilder, jw);
}
- static void writeLastField(final String fieldName, @Nullable final CharSequence value, StringBuilder replaceBuilder, final JsonWriter jw) {
+ public static void writeLastField(final String fieldName, @Nullable final CharSequence value, StringBuilder replaceBuilder, final JsonWriter jw) {
writeFieldName(fieldName, jw);
if (value != null && value.length() > 0) {
writeStringValue(value, replaceBuilder, jw);
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/report/serialize/MetricRegistryReporter.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/serialize/MetricRegistryReporter.java
new file mode 100644
index 0000000000..32c7de89b4
--- /dev/null
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/serialize/MetricRegistryReporter.java
@@ -0,0 +1,71 @@
+/*-
+ * #%L
+ * Elastic APM Java agent
+ * %%
+ * Copyright (C) 2018 - 2020 Elastic and contributors
+ * %%
+ * 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.
+ * #L%
+ */
+package co.elastic.apm.agent.report.serialize;
+
+import co.elastic.apm.agent.context.AbstractLifecycleListener;
+import co.elastic.apm.agent.impl.ElasticApmTracer;
+import co.elastic.apm.agent.metrics.Labels;
+import co.elastic.apm.agent.metrics.MetricRegistry;
+import co.elastic.apm.agent.metrics.MetricSet;
+import co.elastic.apm.agent.report.Reporter;
+import co.elastic.apm.agent.report.ReporterConfiguration;
+
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+public class MetricRegistryReporter extends AbstractLifecycleListener implements MetricRegistry.MetricsReporter, Runnable {
+
+ private final Reporter reporter;
+ private final ElasticApmTracer tracer;
+ private final MetricRegistry metricRegistry;
+ private final MetricRegistrySerializer serializer;
+
+ public MetricRegistryReporter(ElasticApmTracer tracer) {
+ this.tracer = tracer;
+ this.reporter = tracer.getReporter();
+ this.metricRegistry = tracer.getMetricRegistry();
+ this.serializer = new MetricRegistrySerializer();
+ }
+
+ @Override
+ public void start(ElasticApmTracer tracer) {
+ long intervalMs = tracer.getConfig(ReporterConfiguration.class).getMetricsIntervalMs();
+ if (intervalMs > 0) {
+ tracer.getSharedSingleThreadedPool().scheduleAtFixedRate(this, intervalMs, intervalMs, TimeUnit.MILLISECONDS);
+ }
+ }
+
+ @Override
+ public void run() {
+ metricRegistry.flipPhaseAndReport(this);
+ }
+
+ @Override
+ public void report(Map extends Labels, MetricSet> metricSets) {
+ if (tracer.isRunning()) {
+ reporter.report(serializer.serialize(metricSets));
+ }
+ }
+}
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/report/serialize/MetricRegistrySerializer.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/serialize/MetricRegistrySerializer.java
index 363462f480..bedb18b0ae 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/report/serialize/MetricRegistrySerializer.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/report/serialize/MetricRegistrySerializer.java
@@ -11,9 +11,9 @@
* 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
@@ -28,6 +28,7 @@
import co.elastic.apm.agent.metrics.Labels;
import co.elastic.apm.agent.metrics.MetricSet;
import co.elastic.apm.agent.metrics.Timer;
+import com.dslplatform.json.DslJson;
import com.dslplatform.json.JsonWriter;
import com.dslplatform.json.NumberConverter;
@@ -39,12 +40,22 @@ public class MetricRegistrySerializer {
private static final byte NEW_LINE = '\n';
+ private final DslJson
+ * For internal plugins, the whole package (starting at the {@linkplain #getAdviceClass() advice's} package)
+ * will be loaded from a plugin class loader that has both the agent class loader and the class loader of the class this instruments as
+ * parents.
+ * This class loader is also known as the {@code IndyPluginClassLoader}.
+ * For external plugin, the whole jar will be loaded from the indy plugin class loader.
+ *
+ * The advice methods will be dispatched via an {@code INVOKEDYNAMIC} instruction.
+ * Upon first invocation of an instrumented method,
+ * this will call {@code IndyBootstrap#bootstrap} to determine the target {@link java.lang.invoke.ConstantCallSite}.
+ *
+ *
+ * Things to watch out for when using indy plugins:
+ *
+ *
+ *
+ * Set {@link Advice.OnMethodEnter#inline()} and {@link Advice.OnMethodExit#inline()} to {@code false} on all advices.
+ * As the {@code readOnly} flag in Byte Buddy annotations such as {@link Advice.Return#readOnly()} cannot be used with non
+ * {@linkplain Advice.OnMethodEnter#inline() inlined advices},
+ * use {@link AssignTo} and friends.
+ *
+ *
+ * Both the return type and the arguments of advice methods must not contain types from the agent.
+ * If you'd like to return a span from an advice, for example, return an {@link Object} instead.
+ * When using an {@link Advice.Enter} argument on the {@linkplain Advice.OnMethodExit exit advice},
+ * that argument also must not be an agent type.
+ * Use {@link Object} and cast within the method body instead.
+ * The reason is that the return value will become a local variable in the instrumented method.
+ * Due to OSGi, those methods may not have access to agent types.
+ * Another case is when the instrumented class is inside the bootstrap classloader.
+ *
+ *
+ * When an advice instruments classes in multiple class loaders, the plugin classes will be loaded form multiple class loaders.
+ * In order to still share state across those plugin class loaders,
+ * use {@link co.elastic.apm.agent.sdk.state.GlobalVariables} or {@link co.elastic.apm.agent.sdk.state.GlobalState}.
+ * That's necessary as static variables are scoped to the class loader they are defined in.
+ *
+ *
+ * Don't use {@link ThreadLocal}s as it can lead to class loader leaks.
+ * Use {@link GlobalThreadLocal} instead.
+ *
+ *
+ * This applies to internal plugins only:
+ * Due to the automatic plugin classloader creation that is based on package scanning,
+ * plugins need be in their own uniquely named package.
+ * As the package of the {@link #getAdviceClass()} is used as the root,
+ * all advices have to be at the top level of the plugin.
+ *