From 53e70e0b5f50ada526930962bbbb006a18ba6918 Mon Sep 17 00:00:00 2001
From: Rafael Winterhalter
Date: Mon, 20 Feb 2023 15:43:14 +0100
Subject: [PATCH] \#3030: Reduce dependency on elastic APM tracer in plugins
but rather depend on a more general API.
---
.../elastic/apm/agent/impl/BasicTracer.java | 451 ++++++++++++++++++
.../apm/agent/impl/ElasticApmTracer.java | 423 ++++------------
.../agent/impl/ElasticApmTracerBuilder.java | 4 +-
.../elastic/apm/agent/impl/GlobalTracer.java | 52 +-
.../apm/agent/impl/MetricsAwareTracer.java | 26 +
.../co/elastic/apm/agent/impl/NoopTracer.java | 27 ++
.../apm/agent/impl/SpanAwareTracer.java | 41 ++
.../co/elastic/apm/agent/impl/Tracer.java | 20 +-
.../apm/agent/impl/error/ErrorCapture.java | 6 +-
.../agent/impl/transaction/AbstractSpan.java | 6 +-
.../transaction/MetricsAwareTransaction.java | 91 ++++
.../apm/agent/impl/transaction/Span.java | 3 +-
.../agent/impl/transaction/TraceContext.java | 20 +-
.../agent/impl/transaction/Transaction.java | 68 +--
.../agent/objectpool/ObjectPoolFactory.java | 36 +-
.../objectpool/TestObjectPoolFactory.java | 10 +-
.../helper/ApacheHttpAsyncClientHelper.java | 2 +-
.../pluginapi/CaptureSpanInstrumentation.java | 5 -
.../pluginapi/TracedInstrumentation.java | 14 +-
.../apm/api/AnnotationInheritanceTest.java | 1 +
...bstractAsyncHttpClientInstrumentation.java | 10 +-
.../awssdk/v1/helper/DynamoDbHelper.java | 6 +-
.../apm/agent/awssdk/v1/helper/S3Helper.java | 6 +-
.../apm/agent/awssdk/v1/helper/SQSHelper.java | 6 +-
.../sqs/wrapper/MessageIteratorWrapper.java | 4 +-
.../sqs/wrapper/MessageListWrapper.java | 4 +-
.../wrapper/ReceiveMessageResultWrapper.java | 6 +-
.../apm/agent/awssdk/v1/SQSJmsClientIT.java | 2 +-
.../awssdk/v2/helper/DynamoDbHelper.java | 6 +-
.../apm/agent/awssdk/v2/helper/S3Helper.java | 8 +-
.../apm/agent/awssdk/v2/helper/SQSHelper.java | 8 +-
.../sqs/wrapper/MessageIteratorWrapper.java | 4 +-
.../sqs/wrapper/MessageListWrapper.java | 6 +-
...eSyncClientHandlerInstrumentationTest.java | 4 +-
.../apm/agent/awssdk/v2/SQSJmsClientIT.java | 2 +-
.../AbstractAwsSdkInstrumentationHelper.java | 8 +-
...AbstractDynamoDBInstrumentationHelper.java | 4 +-
.../AbstractMessageIteratorWrapper.java | 6 +-
.../common/AbstractMessageListWrapper.java | 6 +-
.../AbstractS3InstrumentationHelper.java | 4 +-
.../AbstractSQSInstrumentationHelper.java | 12 +-
...stractAwsLambdaHandlerInstrumentation.java | 4 +-
.../RequestHandlerInstrumentation.java | 4 +-
.../RequestStreamHandlerInstrumentation.java | 4 +-
.../helper/PlainTransactionHelper.java | 1 +
.../helper/SNSTransactionHelper.java | 6 +-
.../helper/SQSTransactionHelper.java | 6 +-
.../awslambda/lambdas/AbstractFunction.java | 2 +-
.../awslambda/lambdas/CustomHandler.java | 2 +-
.../lambdas/CustomStreamHandler.java | 2 +-
.../lambdas/StreamHandlerLambdaFunction.java | 2 +-
.../advice/AlibabaMonitorFilterAdvice.java | 4 +-
.../advice/ApacheMonitorFilterAdvice.java | 4 +-
.../agent/dubbo/helper/DubboTraceHelper.java | 4 +-
.../Log4j2ServiceNameInstrumentation.java | 4 +-
.../Log4j2ServiceVersionInstrumentation.java | 4 +-
...searchRestClientInstrumentationHelper.java | 8 +-
.../BaseServerEndpointInstrumentation.java | 4 +-
.../JakartaServerEndpointInstrumentation.java | 4 +-
.../JavaxServerEndpointInstrumentation.java | 4 +-
.../apm/agent/java_ldap/LdapClientAdvice.java | 4 +-
.../agent/javalin/JavalinInstrumentation.java | 2 +-
...eeJaxRsTransactionNameInstrumentation.java | 4 +-
...axJaxRsTransactionNameInstrumentation.java | 4 +-
.../jaxrs/JaxRsOffsetMappingFactory.java | 4 +-
.../JaxRsTransactionNameInstrumentation.java | 8 +-
.../JaxWsTransactionNameInstrumentation.java | 4 +-
.../agent/jdbc/StatementInstrumentation.java | 16 +-
.../jdbc/helper/ConnectionMetaDataTest.java | 4 +-
.../agent/httpserver/HttpHandlerAdvice.java | 11 +-
.../apm/agent/jms/BaseJmsInstrumentation.java | 10 +-
.../agent/jms/JmsInstrumentationHelper.java | 6 +-
.../JmsMessageListenerInstrumentation.java | 4 +-
.../jms/spring/SpringMapMessageListener.java | 2 +-
.../apm/agent/jmx/JmxMetricTracker.java | 1 -
.../apm/agent/jmx/JmxMetricTrackerTest.java | 1 +
.../kafka/KafkaConsumerInstrumentation.java | 2 +-
.../helper/KafkaInstrumentationHelper.java | 8 +-
.../ConsumerRecordsIterableWrapper.java | 6 +-
.../ConsumerRecordsIteratorWrapper.java | 6 +-
.../helper/ConsumerRecordsListWrapper.java | 6 +-
.../KafkaInstrumentationHeadersHelper.java | 8 +-
.../CorrelationIdMapAdapterTest.java | 4 +-
.../MicrometerMetricsReporterTest.java | 1 +
.../apm/agent/mongodb/MongoHelper.java | 6 +-
...dkMeterProviderBuilderInstrumentation.java | 1 +
.../opentelemetry/sdk/OTelSpanBuilder.java | 19 +-
.../agent/opentelemetry/sdk/OTelTracer.java | 8 +-
.../ExternalSpanContextInstrumentation.java | 12 +-
.../elastic/apm/agent/profiler/CallTree.java | 5 +-
.../profiler/ProfilingActivationListener.java | 1 +
.../apm/agent/profiler/ProfilingFactory.java | 2 +-
.../apm/agent/profiler/SamplingProfiler.java | 12 +-
.../agent/profiler/CallTreeSpanifyTest.java | 1 +
.../apm/agent/profiler/CallTreeTest.java | 3 +-
.../profiler/SamplingProfilerQueueTest.java | 4 +-
...rtz1JobTransactionNameInstrumentation.java | 4 +-
...JobTransactionNameInstrumentationTest.java | 8 +-
...rtz2JobTransactionNameInstrumentation.java | 4 +-
...JobTransactionNameInstrumentationTest.java | 8 +-
...ractJobTransactionNameInstrumentation.java | 4 +-
...JobTransactionNameInstrumentationTest.java | 4 +-
.../rabbitmq/AbstractBaseInstrumentation.java | 4 +-
.../agent/rabbitmq/MessageBatchHelper.java | 6 +-
.../rabbitmq/MessageBatchIteratorWrapper.java | 7 +-
.../rabbitmq/MessageBatchListWrapper.java | 6 +-
...qpBatchMessageListenerInstrumentation.java | 8 +-
.../rabbitmq/SpringAmqpTransactionHelper.java | 6 +-
.../rabbitmq/SpringBaseInstrumentation.java | 6 +-
...heduledTransactionNameInstrumentation.java | 4 +-
.../scheduled/TimerTaskInstrumentation.java | 4 +-
.../servlet/JakartaAsyncInstrumentation.java | 2 +-
.../servlet/JavaxAsyncInstrumentation.java | 2 +-
.../apm/agent/servlet/ServletApiAdvice.java | 13 +-
.../servlet/ServletTransactionHelper.java | 6 +-
.../JakartaAsyncContextAdviceHelper.java | 3 +-
.../helper/JavaxAsyncContextAdviceHelper.java | 3 +-
.../servlet/ServletServiceNameHelperTest.java | 4 +-
.../apm/agent/sparkjava/RoutesAdvice.java | 4 +-
.../agent/springwebflux/WebfluxHelper.java | 4 +-
.../testapp/GreetingAnnotated.java | 4 +-
.../testapp/GreetingFunctional.java | 4 +-
.../testapp/GreetingHandler.java | 2 +-
.../SpringTransactionNameInstrumentation.java | 2 +-
.../agent/struts/ExecuteOperationsAdvice.java | 2 +-
.../vertx/AbstractHttpTransactionHelper.java | 3 +-
.../agent/vertx/AbstractVertxWebHelper.java | 4 +-
.../helper/HandlerWithCustomNamedSpan.java | 2 +-
.../apm/agent/vertx/v3/web/WebHelper.java | 6 +-
.../apm/agent/vertx/v4/web/WebHelper.java | 6 +-
130 files changed, 1131 insertions(+), 729 deletions(-)
create mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/impl/BasicTracer.java
create mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/impl/MetricsAwareTracer.java
create mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/impl/SpanAwareTracer.java
create mode 100644 apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/MetricsAwareTransaction.java
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/BasicTracer.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/BasicTracer.java
new file mode 100644
index 00000000000..9166e00e02b
--- /dev/null
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/BasicTracer.java
@@ -0,0 +1,451 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package co.elastic.apm.agent.impl;
+
+import co.elastic.apm.agent.configuration.ServiceInfo;
+import co.elastic.apm.agent.impl.error.ErrorCapture;
+import co.elastic.apm.agent.impl.sampling.Sampler;
+import co.elastic.apm.agent.impl.transaction.*;
+import co.elastic.apm.agent.objectpool.ObjectPool;
+import co.elastic.apm.agent.objectpool.ObjectPoolFactory;
+import co.elastic.apm.agent.sdk.logging.Logger;
+import co.elastic.apm.agent.sdk.logging.LoggerFactory;
+import co.elastic.apm.agent.sdk.weakconcurrent.WeakConcurrent;
+import co.elastic.apm.agent.sdk.weakconcurrent.WeakMap;
+import org.stagemonitor.configuration.ConfigurationOptionProvider;
+
+import javax.annotation.Nullable;
+import java.util.Collections;
+
+public class BasicTracer implements SpanAwareTracer {
+
+ private static final Logger logger = LoggerFactory.getLogger(BasicTracer.class);
+
+ private static final WeakMap serviceInfoByClassLoader = WeakConcurrent.buildMap();
+
+ private final ObjectPoolFactory objectPoolFactory;
+ protected final ObjectPool transactionPool;
+ protected final ObjectPool errorPool;
+ protected final ObjectPool spanPool;
+ protected final ObjectPool spanLinkPool;
+
+ protected Sampler sampler;
+ protected boolean assertionsEnabled;
+
+ protected final ThreadLocal activeStack;
+
+ protected BasicTracer(
+ int maxPooledElements,
+ final int transactionMaxSpans,
+ ObjectPoolFactory objectPoolFactory
+ ) {
+ this.objectPoolFactory = objectPoolFactory;
+ this.transactionPool = objectPoolFactory.createTransactionPool(maxPooledElements, this);
+ // we are assuming that we don't need as many errors as spans or transactions
+ this.errorPool = objectPoolFactory.createErrorPool(maxPooledElements / 2, this);
+ this.spanPool = objectPoolFactory.createSpanPool(maxPooledElements, this);
+ // span links pool allows for 10X the maximum allowed span links per span
+ this.spanLinkPool = objectPoolFactory.createSpanLinkPool(AbstractSpan.MAX_ALLOWED_SPAN_LINKS * 10, this);
+ activeStack = new ThreadLocal() {
+ @Override
+ protected ActiveStack initialValue() {
+ return new ActiveStack(transactionMaxSpans);
+ }
+ };
+ // sets the assertionsEnabled flag to true if indeed enabled
+ boolean assertionsEnabled = false;
+ //noinspection AssertWithSideEffects
+ assert assertionsEnabled = true;
+ this.assertionsEnabled = assertionsEnabled;
+ }
+
+ @Override
+ public ObjectPoolFactory getObjectPoolFactory() {
+ return objectPoolFactory;
+ }
+
+ @Override
+ @Nullable
+ public Transaction startRootTransaction(@Nullable ClassLoader initiatingClassLoader) {
+ return startRootTransaction(sampler, -1, initiatingClassLoader);
+ }
+
+ @Override
+ @Nullable
+ public Transaction startRootTransaction(@Nullable ClassLoader initiatingClassLoader, long epochMicro) {
+ return startRootTransaction(sampler, epochMicro, initiatingClassLoader);
+ }
+
+ @Override
+ @Nullable
+ public Transaction startRootTransaction(Sampler sampler, long epochMicros, @Nullable ClassLoader initiatingClassLoader) {
+ Transaction transaction = null;
+ if (isRunning()) {
+ transaction = createTransaction().startRoot(epochMicros, sampler);
+ afterTransactionStart(initiatingClassLoader, transaction);
+ }
+ return transaction;
+ }
+
+ @Override
+ @Nullable
+ public Transaction startChildTransaction(@Nullable C headerCarrier, TextHeaderGetter textHeadersGetter, @Nullable ClassLoader initiatingClassLoader) {
+ return startChildTransaction(headerCarrier, textHeadersGetter, sampler, -1, initiatingClassLoader);
+ }
+
+ @Override
+ @Nullable
+ public Transaction startChildTransaction(@Nullable C headerCarrier, TextHeaderGetter textHeadersGetter, @Nullable ClassLoader initiatingClassLoader, long epochMicros) {
+ return startChildTransaction(headerCarrier, textHeadersGetter, sampler, epochMicros, initiatingClassLoader);
+ }
+
+ @Override
+ @Nullable
+ public Transaction startChildTransaction(@Nullable C headerCarrier, TextHeaderGetter textHeadersGetter, Sampler sampler,
+ long epochMicros, @Nullable ClassLoader initiatingClassLoader) {
+ Transaction transaction = null;
+ if (isRunning()) {
+ transaction = createTransaction().start(TraceContext.getFromTraceContextTextHeaders(), headerCarrier,
+ textHeadersGetter, epochMicros, sampler);
+ afterTransactionStart(initiatingClassLoader, transaction);
+ }
+ return transaction;
+ }
+
+ @Override
+ @Nullable
+ public Transaction startChildTransaction(@Nullable C headerCarrier, BinaryHeaderGetter binaryHeadersGetter, @Nullable ClassLoader initiatingClassLoader) {
+ return startChildTransaction(headerCarrier, binaryHeadersGetter, sampler, -1, initiatingClassLoader);
+ }
+
+ @Override
+ @Nullable
+ public Transaction startChildTransaction(@Nullable C headerCarrier, BinaryHeaderGetter binaryHeadersGetter,
+ Sampler sampler, long epochMicros, @Nullable ClassLoader initiatingClassLoader) {
+ Transaction transaction = null;
+ if (isRunning()) {
+ transaction = createTransaction().start(TraceContext.getFromTraceContextBinaryHeaders(), headerCarrier,
+ binaryHeadersGetter, epochMicros, sampler);
+ afterTransactionStart(initiatingClassLoader, transaction);
+ }
+ return transaction;
+ }
+
+ private void afterTransactionStart(@Nullable ClassLoader initiatingClassLoader, Transaction transaction) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("startTransaction {}", transaction);
+ if (logger.isTraceEnabled()) {
+ logger.trace("starting transaction at",
+ new RuntimeException("this exception is just used to record where the transaction has been started from"));
+ }
+ }
+ final ServiceInfo serviceInfo = getServiceInfoForClassLoader(initiatingClassLoader);
+ if (serviceInfo != null) {
+ transaction.getTraceContext().setServiceInfo(serviceInfo.getServiceName(), serviceInfo.getServiceVersion());
+ }
+ }
+
+ protected Transaction createTransaction() {
+ Transaction transaction = transactionPool.createInstance();
+ while (transaction.getReferenceCount() != 0) {
+ logger.warn("Tried to start a transaction with a non-zero reference count {} {}", transaction.getReferenceCount(), transaction);
+ transaction = transactionPool.createInstance();
+ }
+ return transaction;
+ }
+
+ @Override
+ @Nullable
+ public Transaction currentTransaction() {
+ return activeStack.get().currentTransaction();
+ }
+
+ @Override
+ public void endTransaction(Transaction transaction) {
+ if (transaction.isNoop() || !transaction.isSampled()) {
+ transaction.decrementReferences();
+ }
+ }
+
+ @Override
+ public void captureAndReportException(@Nullable Throwable e, ClassLoader initiatingClassLoader) {
+ ErrorCapture errorCapture = captureException(System.currentTimeMillis() * 1000, e, getActive(), initiatingClassLoader);
+ if (errorCapture != null) {
+ errorCapture.end();
+ }
+ }
+
+ @Override
+ public void endSpan(Span span) {
+ if (!span.isSampled() || span.isDiscarded()) {
+ Transaction transaction = span.getTransaction();
+ if (transaction != null) {
+ transaction.captureDroppedSpan(span);
+ }
+ span.decrementReferences();
+ }
+ }
+
+ @Override
+ @Nullable
+ public String captureAndReportException(long epochMicros, @Nullable Throwable e, @Nullable AbstractSpan> parent) {
+ String id = null;
+ ErrorCapture errorCapture = captureException(epochMicros, e, parent, null);
+ if (errorCapture != null) {
+ id = errorCapture.getTraceContext().getId().toString();
+ errorCapture.end();
+ }
+ return id;
+ }
+
+ @Override
+ @Nullable
+ public ErrorCapture captureException(@Nullable Throwable e, @Nullable AbstractSpan> parent, @Nullable ClassLoader initiatingClassLoader) {
+ return captureException(System.currentTimeMillis() * 1000, e, parent, initiatingClassLoader);
+ }
+
+ @Nullable
+ private ErrorCapture captureException(long epochMicros, @Nullable Throwable e, @Nullable AbstractSpan> parent, @Nullable ClassLoader initiatingClassLoader) {
+ if (!isRunning()) {
+ return null;
+ }
+ // note: if we add inheritance support for exception filtering, caching would be required for performance
+ if (e != null && !isIgnoredException(e)) {
+ ErrorCapture error = errorPool.createInstance();
+ error.withTimestamp(epochMicros);
+ error.setException(e);
+ Transaction currentTransaction = currentTransaction();
+ if (currentTransaction != null) {
+ if (currentTransaction.getNameForSerialization().length() > 0) {
+ error.setTransactionName(currentTransaction.getNameForSerialization());
+ }
+ error.setTransactionType(currentTransaction.getType());
+ error.setTransactionSampled(currentTransaction.isSampled());
+ }
+ if (parent != null) {
+ error.asChildOf(parent);
+ // don't discard spans leading up to an error, otherwise they'd point to an invalid parent
+ parent.setNonDiscardable();
+ } else {
+ error.getTraceContext().getId().setToRandomValue();
+ ServiceInfo serviceInfo = getServiceInfoForClassLoader(initiatingClassLoader);
+ if (serviceInfo != null) {
+ error.getTraceContext().setServiceInfo(serviceInfo.getServiceName(), serviceInfo.getServiceVersion());
+ }
+ }
+ return error;
+ }
+ return null;
+ }
+
+ protected boolean isIgnoredException(Throwable e) {
+ return true;
+ }
+
+ @Override
+ @Nullable
+ public AbstractSpan> getActive() {
+ ElasticContext> active = currentContext();
+ return active != null ? active.getSpan() : null;
+ }
+
+ /**
+ * @return the currently active context, {@literal null} if there is none.
+ */
+ @Nullable
+ public ElasticContext> currentContext() {
+ return activeStack.get().currentContext();
+ }
+
+ @Nullable
+ @Override
+ public Span getActiveSpan() {
+ final AbstractSpan> active = getActive();
+ if (active instanceof Span) {
+ return (Span) active;
+ }
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public Span getActiveExitSpan() {
+ final Span span = getActiveSpan();
+ if (span != null && span.isExit()) {
+ return span;
+ }
+ return null;
+ }
+
+ @Override
+ @Nullable
+ public Span createExitChildSpan() {
+ AbstractSpan> active = getActive();
+ if (active == null) {
+ return null;
+ }
+ return active.createExitSpan();
+ }
+
+ @Override
+ public void setServiceInfoForClassLoader(@Nullable ClassLoader classLoader, ServiceInfo serviceInfo) {
+ // overriding the service name/version for the bootstrap class loader is not an actual use-case
+ // null may also mean we don't know about the initiating class loader
+ if (classLoader == null
+ || !serviceInfo.hasServiceName()
+ // if the service name is set explicitly, don't override it
+ || hasServiceName()) {
+ return;
+ }
+
+ logger.debug("Using `{}` as the service name and `{}` as the service version for class loader [{}]", serviceInfo.getServiceName(), serviceInfo.getServiceVersion(), classLoader);
+ if (!serviceInfoByClassLoader.containsKey(classLoader)) {
+ serviceInfoByClassLoader.putIfAbsent(classLoader, serviceInfo);
+ }
+ }
+
+ protected boolean hasServiceName() {
+ return false;
+ }
+
+ @Nullable
+ @Override
+ public ServiceInfo getServiceInfoForClassLoader(@Nullable ClassLoader initiatingClassLoader) {
+ if (initiatingClassLoader == null) {
+ return null;
+ }
+ return serviceInfoByClassLoader.get(initiatingClassLoader);
+ }
+
+ @Override
+ public TraceContext createSpanLink() {
+ return spanLinkPool.createInstance();
+ }
+
+ /**
+ * Starts a span with a given parent context.
+ *
+ * This method makes it possible to start a span after the parent has already ended.
+ *
+ *
+ * @param parentContext the trace context of the parent
+ * @return a new started span
+ */
+ public Span startSpan(TraceContext.ChildContextCreator childContextCreator, T parentContext) {
+ return startSpan(childContextCreator, parentContext, -1);
+ }
+
+ public Span startSpan(AbstractSpan> parent, long epochMicros) {
+ return startSpan(TraceContext.fromParent(), parent, epochMicros);
+ }
+
+ /**
+ * @param parentContext the trace context of the parent
+ * @param epochMicros the start timestamp of the span in microseconds after epoch
+ * @return a new started span
+ * @see #startSpan(TraceContext.ChildContextCreator, Object)
+ */
+ public Span startSpan(TraceContext.ChildContextCreator childContextCreator, T parentContext, long epochMicros) {
+ return createSpan().start(childContextCreator, parentContext, epochMicros);
+ }
+
+ private Span createSpan() {
+ Span span = spanPool.createInstance();
+ while (span.getReferenceCount() != 0) {
+ logger.warn("Tried to start a span with a non-zero reference count {} {}", span.getReferenceCount(), span);
+ span = spanPool.createInstance();
+ }
+ return span;
+ }
+
+ @Override
+ public void recycle(Transaction transaction) {
+ transactionPool.recycle(transaction);
+ }
+
+ @Override
+ public void recycle(Span span) {
+ spanPool.recycle(span);
+ }
+
+ @Override
+ public void recycle(ErrorCapture error) {
+ errorPool.recycle(error);
+ }
+
+ @Override
+ public void recycle(TraceContext traceContext) {
+ spanLinkPool.recycle(traceContext);
+ }
+
+ @Override
+ public void activate(ElasticContext> context) {
+ activeStack.get().activate(context, Collections.emptyList());
+ }
+
+ @Override
+ public void deactivate(ElasticContext> context) {
+ activeStack.get().deactivate(context, Collections.emptyList(), assertionsEnabled);
+ }
+
+ @Override
+ public Scope activateInScope(final ElasticContext> context) {
+ // already in scope
+ if (currentContext() == context) {
+ return Scope.NoopScope.INSTANCE;
+ }
+ context.activate();
+
+ if (context instanceof Scope) {
+ // we can take shortcut and avoid creating a separate object
+ return (Scope) context;
+ }
+ return new Scope() {
+ @Override
+ public void close() {
+ context.deactivate();
+ }
+ };
+ }
+
+ @Override
+ public void endError(ErrorCapture errorCapture) { }
+
+ @Override
+ public void stop() { }
+
+ @Override
+ public boolean isRunning() {
+ return true;
+ }
+
+ @Override
+ public TracerState getState() {
+ return TracerState.RUNNING;
+ }
+
+ @Override
+ public T getConfig(Class configProvider) {
+ try {
+ return configProvider.getConstructor().newInstance();
+ } catch (Exception e) {
+ throw new IllegalStateException("Failed to create empty configuration for " + configProvider.getName(), e);
+ }
+ }
+}
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 4ceafc421fa..bbbed71fd7d 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
@@ -19,6 +19,7 @@
package co.elastic.apm.agent.impl;
import co.elastic.apm.agent.common.JvmRuntimeInfo;
+import co.elastic.apm.agent.common.util.WildcardMatcher;
import co.elastic.apm.agent.configuration.CoreConfiguration;
import co.elastic.apm.agent.configuration.ServiceInfo;
import co.elastic.apm.agent.configuration.SpanConfiguration;
@@ -30,16 +31,11 @@
import co.elastic.apm.agent.impl.sampling.Sampler;
import co.elastic.apm.agent.impl.stacktrace.StacktraceConfiguration;
import co.elastic.apm.agent.impl.transaction.AbstractSpan;
-import co.elastic.apm.agent.impl.transaction.BinaryHeaderGetter;
import co.elastic.apm.agent.impl.transaction.ElasticContext;
import co.elastic.apm.agent.impl.transaction.Span;
-import co.elastic.apm.agent.impl.transaction.TextHeaderGetter;
-import co.elastic.apm.agent.impl.transaction.TraceContext;
import co.elastic.apm.agent.impl.transaction.Transaction;
import co.elastic.apm.agent.logging.LoggingConfiguration;
-import co.elastic.apm.agent.common.util.WildcardMatcher;
import co.elastic.apm.agent.metrics.MetricRegistry;
-import co.elastic.apm.agent.objectpool.ObjectPool;
import co.elastic.apm.agent.objectpool.ObjectPoolFactory;
import co.elastic.apm.agent.report.ApmServerClient;
import co.elastic.apm.agent.report.Reporter;
@@ -71,7 +67,7 @@
* Note that this is a internal API, so there are no guarantees in terms of backwards compatibility.
*
*/
-public class ElasticApmTracer implements Tracer {
+public class ElasticApmTracer extends BasicTracer implements MetricsAwareTracer {
private static final Logger logger = LoggerFactory.getLogger(ElasticApmTracer.class);
private static final WeakMap serviceInfoByClassLoader = WeakConcurrent.buildMap();
@@ -80,29 +76,13 @@ public class ElasticApmTracer implements Tracer {
private final StacktraceConfiguration stacktraceConfiguration;
private final ApmServerClient apmServerClient;
private final List lifecycleListeners = new CopyOnWriteArrayList<>();
- private final ObjectPool transactionPool;
- private final ObjectPool spanPool;
- private final ObjectPool errorPool;
- private final ObjectPool spanLinkPool;
private final Reporter reporter;
- private final ObjectPoolFactory objectPoolFactory;
-
- private final ThreadLocal activeStack = new ThreadLocal() {
- @Override
- protected ActiveStack initialValue() {
- return new ActiveStack(transactionMaxSpans);
- }
- };
-
+ private final int approximateContextSize;
private final CoreConfiguration coreConfiguration;
- private final int transactionMaxSpans;
private final SpanConfiguration spanConfiguration;
private final List activationListeners;
private final MetricRegistry metricRegistry;
private final ScheduledThreadPoolExecutor sharedPool;
- private final int approximateContextSize;
- private Sampler sampler;
- boolean assertionsEnabled = false;
/**
* The tracer state is volatile to ensure thread safety when queried through {@link ElasticApmTracer#isRunning()} or
@@ -116,263 +96,124 @@ protected ActiveStack initialValue() {
private final String ephemeralId;
private final MetaDataFuture metaDataFuture;
- ElasticApmTracer(ConfigurationRegistry configurationRegistry, MetricRegistry metricRegistry, Reporter reporter, ObjectPoolFactory poolFactory,
- ApmServerClient apmServerClient, final String ephemeralId, MetaDataFuture metaDataFuture) {
- this.metricRegistry = metricRegistry;
- this.configurationRegistry = configurationRegistry;
- this.reporter = reporter;
- this.stacktraceConfiguration = configurationRegistry.getConfig(StacktraceConfiguration.class);
- this.apmServerClient = apmServerClient;
- this.ephemeralId = ephemeralId;
- this.metaDataFuture = metaDataFuture;
+ static ElasticApmTracer of(
+ ConfigurationRegistry configurationRegistry,
+ MetricRegistry metricRegistry,
+ Reporter reporter,
+ ObjectPoolFactory objectPoolFactory,
+ ApmServerClient apmServerClient,
+ final String ephemeralId,
+ MetaDataFuture metaDataFuture) {
+
int maxPooledElements = configurationRegistry.getConfig(ReporterConfiguration.class).getMaxQueueSize() * 2;
- coreConfiguration = configurationRegistry.getConfig(CoreConfiguration.class);
- transactionMaxSpans = coreConfiguration.getTransactionMaxSpans();
- spanConfiguration = configurationRegistry.getConfig(SpanConfiguration.class);
+ CoreConfiguration coreConfiguration = configurationRegistry.getConfig(CoreConfiguration.class);
+ int transactionMaxSpans = coreConfiguration.getTransactionMaxSpans();
+ SpanConfiguration spanConfiguration = configurationRegistry.getConfig(SpanConfiguration.class);
TracerConfiguration tracerConfiguration = configurationRegistry.getConfig(TracerConfiguration.class);
- recordingConfigOptionSet = tracerConfiguration.getRecordingConfig().get();
- tracerConfiguration.getRecordingConfig().addChangeListener(new ConfigurationOption.ChangeListener() {
- @Override
- public void onChange(ConfigurationOption> configurationOption, Boolean oldValue, Boolean newValue) {
- ElasticApmTracer.this.recordingConfigChanged(oldValue, newValue);
- }
- });
+ boolean recordingConfigOptionSet = tracerConfiguration.getRecordingConfig().get();
- this.objectPoolFactory = poolFactory;
- transactionPool = poolFactory.createTransactionPool(maxPooledElements, this);
- spanPool = poolFactory.createSpanPool(maxPooledElements, this);
+ StacktraceConfiguration stacktraceConfiguration = configurationRegistry.getConfig(StacktraceConfiguration.class);
- // we are assuming that we don't need as many errors as spans or transactions
- errorPool = poolFactory.createErrorPool(maxPooledElements / 2, this);
+ Sampler sampler = ProbabilitySampler.of(coreConfiguration.getSampleRate().get());
+ ScheduledThreadPoolExecutor sharedPool = ExecutorUtils.createSingleThreadSchedulingDaemonPool("shared");
- // span links pool allows for 10X the maximum allowed span links per span
- spanLinkPool = poolFactory.createSpanLinkPool(AbstractSpan.MAX_ALLOWED_SPAN_LINKS * 10, this);
-
- sampler = ProbabilitySampler.of(coreConfiguration.getSampleRate().get());
+ // The estimated number of wrappers is linear to the number of the number of external/OTel plugins
+ // - for an internal agent context, there will be at most one wrapper per external/OTel plugin.
+ // - for a context created by an external/OTel, we have one less wrapper required
+ int approximateContextSize = coreConfiguration.getExternalPluginsCount() + 1; // +1 extra is for the OTel API plugin
+
+ return new ElasticApmTracer(
+ maxPooledElements,
+ transactionMaxSpans,
+ approximateContextSize,
+ configurationRegistry,
+ stacktraceConfiguration,
+ apmServerClient,
+ reporter,
+ objectPoolFactory,
+ coreConfiguration,
+ tracerConfiguration,
+ spanConfiguration,
+ metricRegistry,
+ sharedPool,
+ sampler,
+ recordingConfigOptionSet,
+ ephemeralId,
+ metaDataFuture);
+ }
+
+ private ElasticApmTracer(
+ int maxPooledElements,
+ int transactionMaxSpans,
+ int approximateContextSize,
+ ConfigurationRegistry configurationRegistry,
+ StacktraceConfiguration stacktraceConfiguration,
+ ApmServerClient apmServerClient,
+ Reporter reporter,
+ ObjectPoolFactory objectPoolFactory,
+ CoreConfiguration coreConfiguration,
+ TracerConfiguration tracerConfiguration,
+ SpanConfiguration spanConfiguration,
+ MetricRegistry metricRegistry,
+ ScheduledThreadPoolExecutor sharedPool,
+ Sampler sampler,
+ boolean recordingConfigOptionSet,
+ String ephemeralId,
+ MetaDataFuture metaDataFuture) {
+ super(maxPooledElements, transactionMaxSpans, objectPoolFactory);
+ this.configurationRegistry = configurationRegistry;
+ this.approximateContextSize = approximateContextSize;
+ this.stacktraceConfiguration = stacktraceConfiguration;
+ this.apmServerClient = apmServerClient;
+ this.reporter = reporter;
+ this.coreConfiguration = coreConfiguration;
+ this.spanConfiguration = spanConfiguration;
+ this.activationListeners = DependencyInjectingServiceLoader.load(ActivationListener.class, this);
+ this.metricRegistry = metricRegistry;
+ this.sharedPool = sharedPool;
+ this.sampler = sampler;
+ this.recordingConfigOptionSet = recordingConfigOptionSet;
+ this.ephemeralId = ephemeralId;
+ this.metaDataFuture = metaDataFuture;
coreConfiguration.getSampleRate().addChangeListener(new ConfigurationOption.ChangeListener() {
@Override
public void onChange(ConfigurationOption> configurationOption, Double oldValue, Double newValue) {
- sampler = ProbabilitySampler.of(newValue);
+ ElasticApmTracer.this.sampler = ProbabilitySampler.of(newValue);
+ }
+ });
+ tracerConfiguration.getRecordingConfig().addChangeListener(new ConfigurationOption.ChangeListener() {
+ @Override
+ public void onChange(ConfigurationOption> configurationOption, Boolean oldValue, Boolean newValue) {
+ ElasticApmTracer.this.recordingConfigChanged(oldValue, newValue);
}
});
- this.activationListeners = DependencyInjectingServiceLoader.load(ActivationListener.class, this);
- sharedPool = ExecutorUtils.createSingleThreadSchedulingDaemonPool("shared");
-
- // The estimated number of wrappers is linear to the number of the number of external/OTel plugins
- // - for an internal agent context, there will be at most one wrapper per external/OTel plugin.
- // - for a context created by an external/OTel, we have one less wrapper required
- approximateContextSize = coreConfiguration.getExternalPluginsCount() + 1; // +1 extra is for the OTel API plugin
-
- // sets the assertionsEnabled flag to true if indeed enabled
- //noinspection AssertWithSideEffects
- assert assertionsEnabled = true;
- }
-
- @Override
- @Nullable
- public Transaction startRootTransaction(@Nullable ClassLoader initiatingClassLoader) {
- return startRootTransaction(sampler, -1, initiatingClassLoader);
- }
-
- @Override
- @Nullable
- public Transaction startRootTransaction(@Nullable ClassLoader initiatingClassLoader, long epochMicro) {
- return startRootTransaction(sampler, epochMicro, initiatingClassLoader);
- }
-
- @Override
- @Nullable
- public Transaction startRootTransaction(Sampler sampler, long epochMicros, @Nullable ClassLoader initiatingClassLoader) {
- Transaction transaction = null;
- if (isRunning()) {
- transaction = createTransaction().startRoot(epochMicros, sampler);
- afterTransactionStart(initiatingClassLoader, transaction);
- }
- return transaction;
- }
-
- @Override
- @Nullable
- public Transaction startChildTransaction(@Nullable C headerCarrier, TextHeaderGetter textHeadersGetter, @Nullable ClassLoader initiatingClassLoader) {
- return startChildTransaction(headerCarrier, textHeadersGetter, sampler, -1, initiatingClassLoader);
- }
-
- @Override
- @Nullable
- public Transaction startChildTransaction(@Nullable C headerCarrier, TextHeaderGetter textHeadersGetter, @Nullable ClassLoader initiatingClassLoader, long epochMicros) {
- return startChildTransaction(headerCarrier, textHeadersGetter, sampler, epochMicros, initiatingClassLoader);
- }
-
- @Override
- @Nullable
- public Transaction startChildTransaction(@Nullable C headerCarrier, TextHeaderGetter textHeadersGetter, Sampler sampler,
- long epochMicros, @Nullable ClassLoader initiatingClassLoader) {
- Transaction transaction = null;
- if (isRunning()) {
- transaction = createTransaction().start(TraceContext.getFromTraceContextTextHeaders(), headerCarrier,
- textHeadersGetter, epochMicros, sampler);
- afterTransactionStart(initiatingClassLoader, transaction);
- }
- return transaction;
}
@Override
- @Nullable
- public Transaction startChildTransaction(@Nullable C headerCarrier, BinaryHeaderGetter binaryHeadersGetter, @Nullable ClassLoader initiatingClassLoader) {
- return startChildTransaction(headerCarrier, binaryHeadersGetter, sampler, -1, initiatingClassLoader);
+ protected boolean isIgnoredException(Throwable e) {
+ return WildcardMatcher.isAnyMatch(coreConfiguration.getIgnoreExceptions(), e.getClass().getName());
}
@Override
- @Nullable
- public Transaction startChildTransaction(@Nullable C headerCarrier, BinaryHeaderGetter binaryHeadersGetter,
- Sampler sampler, long epochMicros, @Nullable ClassLoader initiatingClassLoader) {
- Transaction transaction = null;
- if (isRunning()) {
- transaction = createTransaction().start(TraceContext.getFromTraceContextBinaryHeaders(), headerCarrier,
- binaryHeadersGetter, epochMicros, sampler);
- afterTransactionStart(initiatingClassLoader, transaction);
- }
- return transaction;
- }
-
- private void afterTransactionStart(@Nullable ClassLoader initiatingClassLoader, Transaction transaction) {
- if (logger.isDebugEnabled()) {
- logger.debug("startTransaction {}", transaction);
- if (logger.isTraceEnabled()) {
- logger.trace("starting transaction at",
- new RuntimeException("this exception is just used to record where the transaction has been started from"));
- }
- }
- final ServiceInfo serviceInfo = getServiceInfoForClassLoader(initiatingClassLoader);
- if (serviceInfo != null) {
- transaction.getTraceContext().setServiceInfo(serviceInfo.getServiceName(), serviceInfo.getServiceVersion());
- }
+ protected boolean hasServiceName() {
+ return coreConfiguration.getServiceNameConfig().getUsedKey() != null;
}
public Transaction noopTransaction() {
return createTransaction().startNoop();
}
- private Transaction createTransaction() {
- Transaction transaction = transactionPool.createInstance();
- while (transaction.getReferenceCount() != 0) {
- logger.warn("Tried to start a transaction with a non-zero reference count {} {}", transaction.getReferenceCount(), transaction);
- transaction = transactionPool.createInstance();
- }
- return transaction;
- }
-
- @Override
- @Nullable
- public Transaction currentTransaction() {
- return activeStack.get().currentTransaction();
- }
-
- /**
- * Starts a span with a given parent context.
- *
- * This method makes it possible to start a span after the parent has already ended.
- *
- *
- * @param parentContext the trace context of the parent
- * @return a new started span
- */
- public Span startSpan(TraceContext.ChildContextCreator childContextCreator, T parentContext) {
- return startSpan(childContextCreator, parentContext, -1);
- }
-
- public Span startSpan(AbstractSpan> parent, long epochMicros) {
- return startSpan(TraceContext.fromParent(), parent, epochMicros);
- }
-
- /**
- * @param parentContext the trace context of the parent
- * @param epochMicros the start timestamp of the span in microseconds after epoch
- * @return a new started span
- * @see #startSpan(TraceContext.ChildContextCreator, Object)
- */
- public Span startSpan(TraceContext.ChildContextCreator childContextCreator, T parentContext, long epochMicros) {
- return createSpan().start(childContextCreator, parentContext, epochMicros);
- }
-
- private Span createSpan() {
- Span span = spanPool.createInstance();
- while (span.getReferenceCount() != 0) {
- logger.warn("Tried to start a span with a non-zero reference count {} {}", span.getReferenceCount(), span);
- span = spanPool.createInstance();
- }
- return span;
- }
-
- @Override
- public void captureAndReportException(@Nullable Throwable e, ClassLoader initiatingClassLoader) {
- ErrorCapture errorCapture = captureException(System.currentTimeMillis() * 1000, e, getActive(), initiatingClassLoader);
- if (errorCapture != null) {
- errorCapture.end();
- }
- }
-
- @Override
- @Nullable
- public String captureAndReportException(long epochMicros, @Nullable Throwable e, @Nullable AbstractSpan> parent) {
- String id = null;
- ErrorCapture errorCapture = captureException(epochMicros, e, parent, null);
- if (errorCapture != null) {
- id = errorCapture.getTraceContext().getId().toString();
- errorCapture.end();
- }
- return id;
- }
-
- @Override
- @Nullable
- public ErrorCapture captureException(@Nullable Throwable e, @Nullable AbstractSpan> parent, @Nullable ClassLoader initiatingClassLoader) {
- return captureException(System.currentTimeMillis() * 1000, e, parent, initiatingClassLoader);
- }
-
- @Nullable
- private ErrorCapture captureException(long epochMicros, @Nullable Throwable e, @Nullable AbstractSpan> parent, @Nullable ClassLoader initiatingClassLoader) {
- if (!isRunning()) {
- return null;
- }
- // note: if we add inheritance support for exception filtering, caching would be required for performance
- if (e != null && !WildcardMatcher.isAnyMatch(coreConfiguration.getIgnoreExceptions(), e.getClass().getName())) {
- ErrorCapture error = errorPool.createInstance();
- error.withTimestamp(epochMicros);
- error.setException(e);
- Transaction currentTransaction = currentTransaction();
- if (currentTransaction != null) {
- if (currentTransaction.getNameForSerialization().length() > 0) {
- error.setTransactionName(currentTransaction.getNameForSerialization());
- }
- error.setTransactionType(currentTransaction.getType());
- error.setTransactionSampled(currentTransaction.isSampled());
- }
- if (parent != null) {
- error.asChildOf(parent);
- // don't discard spans leading up to an error, otherwise they'd point to an invalid parent
- parent.setNonDiscardable();
- } else {
- error.getTraceContext().getId().setToRandomValue();
- ServiceInfo serviceInfo = getServiceInfoForClassLoader(initiatingClassLoader);
- if (serviceInfo != null) {
- error.getTraceContext().setServiceInfo(serviceInfo.getServiceName(), serviceInfo.getServiceVersion());
- }
- }
- return error;
- }
- return null;
- }
-
public ConfigurationRegistry getConfigurationRegistry() {
return configurationRegistry;
}
+ @Override
public T getConfig(Class configProvider) {
return configurationRegistry.getConfig(configProvider);
}
+ @Override
public void endTransaction(Transaction transaction) {
if (logger.isDebugEnabled()) {
logger.debug("endTransaction {}", transaction);
@@ -390,6 +231,7 @@ public void endTransaction(Transaction transaction) {
}
}
+ @Override
public void endSpan(Span span) {
if (logger.isDebugEnabled()) {
logger.debug("endSpan {}", span);
@@ -451,30 +293,12 @@ private void reportSpan(Span span) {
reporter.report(span);
}
+ @Override
public void endError(ErrorCapture error) {
reporter.report(error);
}
- public TraceContext createSpanLink() {
- return spanLinkPool.createInstance();
- }
-
- public void recycle(Transaction transaction) {
- transactionPool.recycle(transaction);
- }
-
- public void recycle(Span span) {
- spanPool.recycle(span);
- }
-
- public void recycle(ErrorCapture error) {
- errorPool.recycle(error);
- }
-
- public void recycle(TraceContext traceContext) {
- spanLinkPool.recycle(traceContext);
- }
-
+ @Override
public synchronized void stop() {
if (tracerState == TracerState.STOPPED) {
// may happen if explicitly stopped in a unit test and executed again within a shutdown hook
@@ -511,37 +335,6 @@ public Sampler getSampler() {
return sampler;
}
- public ObjectPoolFactory getObjectPoolFactory() {
- return objectPoolFactory;
- }
-
- @Override
- @Nullable
- public AbstractSpan> getActive() {
- ElasticContext> active = currentContext();
- return active != null ? active.getSpan() : null;
- }
-
- @Nullable
- @Override
- public Span getActiveSpan() {
- final AbstractSpan> active = getActive();
- if (active instanceof Span) {
- return (Span) active;
- }
- return null;
- }
-
- @Nullable
- @Override
- public Span getActiveExitSpan() {
- final Span span = getActiveSpan();
- if (span != null && span.isExit()) {
- return span;
- }
- return null;
- }
-
public void registerSpanListener(ActivationListener activationListener) {
this.activationListeners.add(activationListener);
}
@@ -723,14 +516,6 @@ public T getLifecycleListener(Class listenerClass) {
return null;
}
- /**
- * @return the currently active context, {@literal null} if there is none.
- */
- @Nullable
- public ElasticContext> currentContext() {
- return activeStack.get().currentContext();
- }
-
/**
* Lazily wraps the currently active context if required, wrapper instance is cached with wrapperClass as key.
* Wrapping is transparently handled by {@link #currentContext()}.
@@ -744,33 +529,17 @@ public > T wrapActiveContextIfRequired(Class wrap
return activeStack.get().wrapActiveContextIfRequired(wrapperClass, wrapFunction, approximateContextSize);
}
+ @Override
public void activate(ElasticContext> context) {
activeStack.get().activate(context, activationListeners);
}
- public Scope activateInScope(final ElasticContext> context) {
- // already in scope
- if (currentContext() == context) {
- return Scope.NoopScope.INSTANCE;
- }
- context.activate();
-
- if (context instanceof Scope) {
- // we can take shortcut and avoid creating a separate object
- return (Scope) context;
- }
- return new Scope() {
- @Override
- public void close() {
- context.deactivate();
- }
- };
- }
-
+ @Override
public void deactivate(ElasticContext> context) {
activeStack.get().deactivate(context, activationListeners, assertionsEnabled);
}
+ @Override
public MetricRegistry getMetricRegistry() {
return metricRegistry;
}
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 cd43e22600f..b132633f08c 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
@@ -99,7 +99,7 @@ public ElasticApmTracerBuilder(List configSources) {
this.ephemeralId = UUID.randomUUID().toString();
LoggingConfiguration.init(configSources, ephemeralId);
logger = LoggerFactory.getLogger(getClass());
- objectPoolFactory = new ObjectPoolFactory();
+ objectPoolFactory = ObjectPoolFactory.INSTANCE;
extraLifecycleListeners = new ArrayList<>();
}
@@ -182,7 +182,7 @@ private ElasticApmTracer build(boolean startTracer) {
reporter = new ReporterFactory().createReporter(configurationRegistry, apmServerClient, metaDataFuture, healthMetrics);
}
- ElasticApmTracer tracer = new ElasticApmTracer(configurationRegistry, metricRegistry, reporter, objectPoolFactory, apmServerClient, ephemeralId, metaDataFuture);
+ ElasticApmTracer tracer = ElasticApmTracer.of(configurationRegistry, metricRegistry, reporter, objectPoolFactory, apmServerClient, ephemeralId, metaDataFuture);
lifecycleListeners.addAll(DependencyInjectingServiceLoader.load(LifecycleListener.class, tracer));
lifecycleListeners.addAll(extraLifecycleListeners);
tracer.init(lifecycleListeners);
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 017be270c83..b8821fc0002 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
@@ -26,8 +26,10 @@
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 co.elastic.apm.agent.objectpool.ObjectPoolFactory;
import co.elastic.apm.agent.util.PrivilegedActionUtils;
import co.elastic.apm.agent.util.VersionUtils;
+import org.stagemonitor.configuration.ConfigurationOptionProvider;
import javax.annotation.Nullable;
import java.io.File;
@@ -51,14 +53,23 @@ public static Tracer get() {
}
@Nullable
- public static ElasticApmTracer getTracerImpl() {
+ public static T get(Class type) {
Tracer tracer = INSTANCE.tracer;
- if (tracer instanceof ElasticApmTracer) {
- return ((ElasticApmTracer) tracer);
+ if (type.isInstance(tracer)) {
+ return type.cast(tracer);
}
return null;
}
+ public static T require(Class type) {
+ return Objects.requireNonNull(get(type), "Registered tracer is not an instance of " + type.getName());
+ }
+
+ @Nullable
+ public static ElasticApmTracer getTracerImpl() {
+ return get(ElasticApmTracer.class);
+ }
+
public static ElasticApmTracer requireTracerImpl() {
return Objects.requireNonNull(getTracerImpl(), "Registered tracer is not an instance of ElasticApmTracer");
}
@@ -231,4 +242,39 @@ public boolean isRunning() {
public Span createExitChildSpan() {
return tracer.createExitChildSpan();
}
+
+ @Override
+ public void recycle(Transaction transaction) {
+ tracer.recycle(transaction);
+ }
+
+ @Override
+ public void endSpan(Span span) {
+ tracer.endSpan(span);
+ }
+
+ @Override
+ public void endTransaction(Transaction transaction) {
+ tracer.endTransaction(transaction);
+ }
+
+ @Override
+ public void endError(ErrorCapture errorCapture) {
+ tracer.endError(errorCapture);
+ }
+
+ @Override
+ public T getConfig(Class configuration) {
+ return tracer.getConfig(configuration);
+ }
+
+ @Override
+ public ObjectPoolFactory getObjectPoolFactory() {
+ return tracer.getObjectPoolFactory();
+ }
+
+ @Override
+ public void recycle(ErrorCapture errorCapture) {
+ tracer.recycle(errorCapture);
+ }
}
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/MetricsAwareTracer.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/MetricsAwareTracer.java
new file mode 100644
index 00000000000..f5792d89987
--- /dev/null
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/MetricsAwareTracer.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package co.elastic.apm.agent.impl;
+
+import co.elastic.apm.agent.metrics.MetricRegistry;
+
+public interface MetricsAwareTracer extends SpanAwareTracer {
+
+ MetricRegistry getMetricRegistry();
+}
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/NoopTracer.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/NoopTracer.java
index b0e35cdf76e..49f58d15651 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/NoopTracer.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/NoopTracer.java
@@ -26,6 +26,8 @@
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 co.elastic.apm.agent.objectpool.ObjectPoolFactory;
+import org.stagemonitor.configuration.ConfigurationOptionProvider;
import javax.annotation.Nullable;
@@ -154,4 +156,29 @@ public boolean isRunning() {
public Span createExitChildSpan() {
return null;
}
+
+ @Override
+ public void recycle(Transaction transaction) { }
+
+ @Override
+ public void recycle(ErrorCapture errorCapture) { }
+
+ @Override
+ public void endSpan(Span span) { }
+
+ @Override
+ public void endTransaction(Transaction transaction) { }
+
+ @Override
+ public void endError(ErrorCapture errorCapture) { }
+
+ @Override
+ public T getConfig(Class configuration) {
+ return null;
+ }
+
+ @Override
+ public ObjectPoolFactory getObjectPoolFactory() {
+ return null;
+ }
}
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/SpanAwareTracer.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/SpanAwareTracer.java
new file mode 100644
index 00000000000..cbb087fcded
--- /dev/null
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/SpanAwareTracer.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package co.elastic.apm.agent.impl;
+
+import co.elastic.apm.agent.impl.transaction.AbstractSpan;
+import co.elastic.apm.agent.impl.transaction.ElasticContext;
+import co.elastic.apm.agent.impl.transaction.Span;
+import co.elastic.apm.agent.impl.transaction.TraceContext;
+
+public interface SpanAwareTracer extends Tracer {
+
+ TraceContext createSpanLink();
+
+ void recycle(TraceContext context);
+
+ void recycle(Span span);
+
+ Span startSpan(AbstractSpan> parent, long epochMicros);
+
+ void activate(ElasticContext> context);
+
+ void deactivate(ElasticContext> context);
+
+ Scope activateInScope(ElasticContext> context);
+}
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/Tracer.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/Tracer.java
index c4b23583b82..904f26c8a88 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/Tracer.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/Tracer.java
@@ -27,6 +27,8 @@
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 co.elastic.apm.agent.objectpool.ObjectPoolFactory;
+import org.stagemonitor.configuration.ConfigurationOptionProvider;
import javax.annotation.Nullable;
@@ -151,8 +153,6 @@ Transaction startChildTransaction(@Nullable C headerCarrier, BinaryHeaderGet
@Nullable
Span getActiveExitSpan();
- TracerState getState();
-
@Nullable
ServiceInfo getServiceInfoForClassLoader(@Nullable ClassLoader classLoader);
@@ -176,9 +176,25 @@ Transaction startChildTransaction(@Nullable C headerCarrier, BinaryHeaderGet
boolean isRunning();
+ TracerState getState();
+
@Nullable
Span createExitChildSpan();
+ void recycle(Transaction transaction);
+
+ void recycle(ErrorCapture errorCapture);
+
+ void endSpan(Span span);
+
+ void endTransaction(Transaction transaction);
+
+ void endError(ErrorCapture errorCapture);
+
+ T getConfig(Class configuration);
+
+ ObjectPoolFactory getObjectPoolFactory();
+
/**
* An enumeration used to represent the current tracer state.
*/
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/error/ErrorCapture.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/error/ErrorCapture.java
index 2f1a503a9b8..927073d15ed 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/error/ErrorCapture.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/error/ErrorCapture.java
@@ -19,7 +19,7 @@
package co.elastic.apm.agent.impl.error;
import co.elastic.apm.agent.configuration.CoreConfiguration;
-import co.elastic.apm.agent.impl.ElasticApmTracer;
+import co.elastic.apm.agent.impl.Tracer;
import co.elastic.apm.agent.impl.context.TransactionContext;
import co.elastic.apm.agent.impl.stacktrace.StacktraceConfiguration;
import co.elastic.apm.agent.impl.transaction.AbstractSpan;
@@ -52,7 +52,7 @@ public class ErrorCapture implements Recyclable {
* Any arbitrary contextual information regarding the event, captured by the agent, optionally provided by the user
*/
private final TransactionContext context = new TransactionContext();
- private final ElasticApmTracer tracer;
+ private final Tracer tracer;
/**
* Information about the originally thrown error.
*/
@@ -71,7 +71,7 @@ public class ErrorCapture implements Recyclable {
private final StringBuilder culprit = new StringBuilder();
- public ErrorCapture(ElasticApmTracer tracer) {
+ public ErrorCapture(Tracer tracer) {
this.tracer = tracer;
traceContext = TraceContext.with128BitId(this.tracer);
}
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/AbstractSpan.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/AbstractSpan.java
index 4ea3d06eb38..1be9e7c70d3 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/AbstractSpan.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/AbstractSpan.java
@@ -20,8 +20,8 @@
import co.elastic.apm.agent.collections.LongList;
import co.elastic.apm.agent.configuration.CoreConfiguration;
-import co.elastic.apm.agent.impl.ElasticApmTracer;
import co.elastic.apm.agent.impl.Scope;
+import co.elastic.apm.agent.impl.SpanAwareTracer;
import co.elastic.apm.agent.impl.context.AbstractContext;
import co.elastic.apm.agent.common.util.WildcardMatcher;
import co.elastic.apm.agent.objectpool.Recyclable;
@@ -56,7 +56,7 @@ public abstract class AbstractSpan> implements Recycla
*/
protected final StringBuilder name = new StringBuilder();
protected final boolean collectBreakdownMetrics;
- protected final ElasticApmTracer tracer;
+ protected final SpanAwareTracer tracer;
protected final AtomicLong timestamp = new AtomicLong();
protected final AtomicLong endTimestamp = new AtomicLong();
@@ -210,7 +210,7 @@ public long getDuration() {
}
}
- public AbstractSpan(ElasticApmTracer tracer) {
+ public AbstractSpan(SpanAwareTracer tracer) {
this.tracer = tracer;
traceContext = TraceContext.with64BitId(this.tracer);
boolean selfTimeCollectionEnabled = !WildcardMatcher.isAnyMatch(tracer.getConfig(ReporterConfiguration.class).getDisableMetrics(), "span.self_time");
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/MetricsAwareTransaction.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/MetricsAwareTransaction.java
new file mode 100644
index 00000000000..4a17108a99a
--- /dev/null
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/MetricsAwareTransaction.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package co.elastic.apm.agent.impl.transaction;
+
+import co.elastic.apm.agent.impl.MetricsAwareTracer;
+import co.elastic.apm.agent.metrics.Labels;
+import co.elastic.apm.agent.metrics.MetricRegistry;
+import co.elastic.apm.agent.metrics.Timer;
+import co.elastic.apm.agent.util.KeyListConcurrentHashMap;
+
+import java.util.List;
+
+public class MetricsAwareTransaction extends Transaction {
+
+ private static final ThreadLocal labelsThreadLocal = new ThreadLocal() {
+ @Override
+ protected Labels.Mutable initialValue() {
+ return Labels.Mutable.of();
+ }
+ };
+
+ public MetricsAwareTransaction(MetricsAwareTracer tracer) {
+ super(tracer);
+ }
+
+ protected void trackMetrics() {
+ try {
+ phaser.readerLock();
+ phaser.flipPhase();
+ // timers are guaranteed to be stable now
+ // - no concurrent updates possible as finished is true
+ // - no other thread is running the incrementTimer method,
+ // as flipPhase only returns when all threads have exited that method
+
+ final String type = getType();
+ if (type == null) {
+ return;
+ }
+ final Labels.Mutable labels = labelsThreadLocal.get();
+ labels.resetState();
+ labels.serviceName(getTraceContext().getServiceName())
+ .serviceVersion(getTraceContext().getServiceVersion())
+ .transactionName(name)
+ .transactionType(type); // TODO:
+ final MetricRegistry metricRegistry = ((MetricsAwareTracer) tracer).getMetricRegistry();
+ long criticalValueAtEnter = metricRegistry.writerCriticalSectionEnter();
+ try {
+ if (collectBreakdownMetrics) {
+ List types = timerBySpanTypeAndSubtype.keyList();
+ for (int i = 0; i < types.size(); i++) {
+ String spanType = types.get(i);
+ KeyListConcurrentHashMap timerBySubtype = timerBySpanTypeAndSubtype.get(spanType);
+ List subtypes = timerBySubtype.keyList();
+ for (int j = 0; j < subtypes.size(); j++) {
+ String subtype = subtypes.get(j);
+ final Timer timer = timerBySubtype.get(subtype);
+ if (timer.getCount() > 0) {
+ if (subtype.equals("")) {
+ subtype = null;
+ }
+ labels.spanType(spanType).spanSubType(subtype);
+ metricRegistry.updateTimer("span.self_time", labels, timer.getTotalTimeUs(), timer.getCount());
+ timer.resetState();
+ }
+ }
+ }
+ }
+ } finally {
+ metricRegistry.writerCriticalSectionExit(criticalValueAtEnter);
+ }
+ } finally {
+ phaser.readerUnlock();
+ }
+ }
+}
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/Span.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/Span.java
index 9ad0f0a6829..fda687afbd3 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/Span.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/Span.java
@@ -20,6 +20,7 @@
import co.elastic.apm.agent.configuration.CoreConfiguration;
import co.elastic.apm.agent.impl.ElasticApmTracer;
+import co.elastic.apm.agent.impl.SpanAwareTracer;
import co.elastic.apm.agent.impl.context.Db;
import co.elastic.apm.agent.impl.context.Message;
import co.elastic.apm.agent.impl.context.ServiceTarget;
@@ -82,7 +83,7 @@ public void setNonDiscardable() {
}
}
- public Span(ElasticApmTracer tracer) {
+ public Span(SpanAwareTracer tracer) {
super(tracer);
}
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/TraceContext.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/TraceContext.java
index bfd849a4e72..75e474fe9c7 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/TraceContext.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/TraceContext.java
@@ -20,6 +20,7 @@
import co.elastic.apm.agent.configuration.CoreConfiguration;
import co.elastic.apm.agent.impl.ElasticApmTracer;
+import co.elastic.apm.agent.impl.GlobalTracer;
import co.elastic.apm.agent.impl.Tracer;
import co.elastic.apm.agent.impl.sampling.Sampler;
import co.elastic.apm.agent.objectpool.Recyclable;
@@ -209,7 +210,7 @@ public static void copyTraceContextTextHeaders(S source, TextHeaderGetter
// ???????0 -> not recorded
private static final byte FLAG_RECORDED = 0b0000_0001;
private final Id traceId = Id.new128BitId();
- private final ElasticApmTracer tracer;
+ private final Tracer tracer;
private final Id id;
private final Id parentId = Id.new64BitId();
private final Id transactionId = Id.new64BitId();
@@ -234,7 +235,7 @@ public static void copyTraceContextTextHeaders(S source, TextHeaderGetter
@Nullable
private String serviceVersion;
- private TraceContext(ElasticApmTracer tracer, Id id) {
+ private TraceContext(Tracer tracer, Id id) {
coreConfiguration = tracer.getConfig(CoreConfiguration.class);
traceState = new TraceState();
traceState.setSizeLimit(coreConfiguration.getTracestateSizeLimit());
@@ -249,7 +250,7 @@ private TraceContext(ElasticApmTracer tracer, Id id) {
*
* @param tracer a valid tracer
*/
- public static TraceContext with64BitId(ElasticApmTracer tracer) {
+ public static TraceContext with64BitId(Tracer tracer) {
return new TraceContext(tracer, Id.new64BitId());
}
@@ -260,7 +261,7 @@ public static TraceContext with64BitId(ElasticApmTracer tracer) {
*
* @param tracer a valid tracer
*/
- public static TraceContext with128BitId(ElasticApmTracer tracer) {
+ public static TraceContext with128BitId(Tracer tracer) {
return new TraceContext(tracer, Id.new128BitId());
}
@@ -701,11 +702,18 @@ public String getServiceVersion() {
}
public Span createSpan() {
- return tracer.startSpan(fromParentContext(), this);
+ return requireTracerImpl().startSpan(fromParentContext(), this);
}
public Span createSpan(long epochMicros) {
- return tracer.startSpan(fromParentContext(), this, epochMicros);
+ return requireTracerImpl().startSpan(fromParentContext(), this, epochMicros);
+ }
+
+ private ElasticApmTracer requireTracerImpl() {
+ if (tracer instanceof ElasticApmTracer) {
+ return (ElasticApmTracer) tracer;
+ }
+ throw new IllegalStateException("Can only start spans from elastic APM tracer");
}
@Override
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/Transaction.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/Transaction.java
index 868c23a119c..29f792cd6bb 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/Transaction.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/Transaction.java
@@ -20,20 +20,17 @@
import co.elastic.apm.agent.configuration.CoreConfiguration;
import co.elastic.apm.agent.configuration.SpanConfiguration;
-import co.elastic.apm.agent.impl.ElasticApmTracer;
+import co.elastic.apm.agent.impl.SpanAwareTracer;
import co.elastic.apm.agent.impl.context.Response;
import co.elastic.apm.agent.impl.context.TransactionContext;
import co.elastic.apm.agent.impl.context.web.ResultUtil;
import co.elastic.apm.agent.impl.sampling.Sampler;
import co.elastic.apm.agent.common.util.WildcardMatcher;
-import co.elastic.apm.agent.metrics.Labels;
-import co.elastic.apm.agent.metrics.MetricRegistry;
import co.elastic.apm.agent.metrics.Timer;
import co.elastic.apm.agent.util.KeyListConcurrentHashMap;
import org.HdrHistogram.WriterReaderPhaser;
import javax.annotation.Nullable;
-import java.util.List;
import static co.elastic.apm.agent.configuration.CoreConfiguration.TraceContinuationStrategy.RESTART;
import static co.elastic.apm.agent.configuration.CoreConfiguration.TraceContinuationStrategy.RESTART_EXTERNAL;
@@ -43,13 +40,6 @@
*/
public class Transaction extends AbstractSpan {
- private static final ThreadLocal labelsThreadLocal = new ThreadLocal() {
- @Override
- protected Labels.Mutable initialValue() {
- return Labels.Mutable.of();
- }
- };
-
public static final String TYPE_REQUEST = "request";
/**
@@ -69,8 +59,8 @@ protected Labels.Mutable initialValue() {
* That is done in order to minimize {@link java.util.Map.Entry} garbage.
*
*/
- private final KeyListConcurrentHashMap> timerBySpanTypeAndSubtype = new KeyListConcurrentHashMap<>();
- private final WriterReaderPhaser phaser = new WriterReaderPhaser();
+ final KeyListConcurrentHashMap> timerBySpanTypeAndSubtype = new KeyListConcurrentHashMap<>();
+ final WriterReaderPhaser phaser = new WriterReaderPhaser();
private final CoreConfiguration coreConfig;
private final SpanConfiguration spanConfig;
@@ -114,7 +104,7 @@ public Transaction getTransaction() {
return this;
}
- public Transaction(ElasticApmTracer tracer) {
+ public Transaction(SpanAwareTracer tracer) {
super(tracer);
coreConfig = tracer.getConfig(CoreConfiguration.class);
spanConfig = tracer.getConfig(SpanConfiguration.class);
@@ -457,53 +447,5 @@ void incrementTimer(@Nullable String type, @Nullable String subtype, long durati
}
}
- private void trackMetrics() {
- try {
- phaser.readerLock();
- phaser.flipPhase();
- // timers are guaranteed to be stable now
- // - no concurrent updates possible as finished is true
- // - no other thread is running the incrementTimer method,
- // as flipPhase only returns when all threads have exited that method
-
- final String type = getType();
- if (type == null) {
- return;
- }
- final Labels.Mutable labels = labelsThreadLocal.get();
- labels.resetState();
- labels.serviceName(getTraceContext().getServiceName())
- .serviceVersion(getTraceContext().getServiceVersion())
- .transactionName(name)
- .transactionType(type);
- final MetricRegistry metricRegistry = tracer.getMetricRegistry();
- long criticalValueAtEnter = metricRegistry.writerCriticalSectionEnter();
- try {
- if (collectBreakdownMetrics) {
- List types = timerBySpanTypeAndSubtype.keyList();
- for (int i = 0; i < types.size(); i++) {
- String spanType = types.get(i);
- KeyListConcurrentHashMap timerBySubtype = timerBySpanTypeAndSubtype.get(spanType);
- List subtypes = timerBySubtype.keyList();
- for (int j = 0; j < subtypes.size(); j++) {
- String subtype = subtypes.get(j);
- final Timer timer = timerBySubtype.get(subtype);
- if (timer.getCount() > 0) {
- if (subtype.equals("")) {
- subtype = null;
- }
- labels.spanType(spanType).spanSubType(subtype);
- metricRegistry.updateTimer("span.self_time", labels, timer.getTotalTimeUs(), timer.getCount());
- timer.resetState();
- }
- }
- }
- }
- } finally {
- metricRegistry.writerCriticalSectionExit(criticalValueAtEnter);
- }
- } finally {
- phaser.readerUnlock();
- }
- }
+ protected void trackMetrics() { }
}
diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/objectpool/ObjectPoolFactory.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/objectpool/ObjectPoolFactory.java
index 4939117fb28..f39f1d1e2f8 100644
--- a/apm-agent-core/src/main/java/co/elastic/apm/agent/objectpool/ObjectPoolFactory.java
+++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/objectpool/ObjectPoolFactory.java
@@ -18,8 +18,10 @@
*/
package co.elastic.apm.agent.objectpool;
-import co.elastic.apm.agent.impl.ElasticApmTracer;
+import co.elastic.apm.agent.impl.MetricsAwareTracer;
+import co.elastic.apm.agent.impl.SpanAwareTracer;
import co.elastic.apm.agent.impl.error.ErrorCapture;
+import co.elastic.apm.agent.impl.transaction.MetricsAwareTransaction;
import co.elastic.apm.agent.impl.transaction.Span;
import co.elastic.apm.agent.impl.transaction.TraceContext;
import co.elastic.apm.agent.impl.transaction.Transaction;
@@ -28,20 +30,32 @@
public class ObjectPoolFactory {
+ public static final ObjectPoolFactory INSTANCE = new ObjectPoolFactory();
+
public ObjectPool createRecyclableObjectPool(int maxCapacity, Allocator allocator) {
return QueueBasedObjectPool.ofRecyclable(new MpmcAtomicArrayQueue((maxCapacity)), false, allocator);
}
- public ObjectPool createTransactionPool(int maxCapacity, final ElasticApmTracer tracer) {
- return createRecyclableObjectPool(maxCapacity, new Allocator() {
- @Override
- public Transaction createInstance() {
- return new Transaction(tracer);
- }
- });
+ public ObjectPool createTransactionPool(int maxCapacity, final SpanAwareTracer tracer) {
+ if (tracer instanceof MetricsAwareTracer) {
+ final MetricsAwareTracer cast = (MetricsAwareTracer) tracer;
+ return createRecyclableObjectPool(maxCapacity, new Allocator() {
+ @Override
+ public Transaction createInstance() {
+ return new MetricsAwareTransaction(cast);
+ }
+ });
+ } else {
+ return createRecyclableObjectPool(maxCapacity, new Allocator() {
+ @Override
+ public Transaction createInstance() {
+ return new Transaction(tracer);
+ }
+ });
+ }
}
- public ObjectPool createSpanPool(int maxCapacity, final ElasticApmTracer tracer) {
+ public ObjectPool createSpanPool(int maxCapacity, final SpanAwareTracer tracer) {
return createRecyclableObjectPool(maxCapacity, new Allocator() {
@Override
public Span createInstance() {
@@ -50,7 +64,7 @@ public Span createInstance() {
});
}
- public ObjectPool createErrorPool(int maxCapacity, final ElasticApmTracer tracer) {
+ public ObjectPool createErrorPool(int maxCapacity, final SpanAwareTracer tracer) {
return createRecyclableObjectPool(maxCapacity, new Allocator() {
@Override
public ErrorCapture createInstance() {
@@ -59,7 +73,7 @@ public ErrorCapture createInstance() {
});
}
- public ObjectPool createSpanLinkPool(int maxCapacity, final ElasticApmTracer tracer) {
+ public ObjectPool createSpanLinkPool(int maxCapacity, final SpanAwareTracer tracer) {
return createRecyclableObjectPool(maxCapacity, new Allocator() {
@Override
public TraceContext createInstance() {
diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/objectpool/TestObjectPoolFactory.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/objectpool/TestObjectPoolFactory.java
index bd3363dc2ab..2c48e38ce76 100644
--- a/apm-agent-core/src/test/java/co/elastic/apm/agent/objectpool/TestObjectPoolFactory.java
+++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/objectpool/TestObjectPoolFactory.java
@@ -18,7 +18,7 @@
*/
package co.elastic.apm.agent.objectpool;
-import co.elastic.apm.agent.impl.ElasticApmTracer;
+import co.elastic.apm.agent.impl.SpanAwareTracer;
import co.elastic.apm.agent.impl.error.ErrorCapture;
import co.elastic.apm.agent.impl.transaction.Span;
import co.elastic.apm.agent.impl.transaction.TraceContext;
@@ -93,25 +93,25 @@ public void reset() {
}
@Override
- public ObjectPool createTransactionPool(int maxCapacity, ElasticApmTracer tracer) {
+ public ObjectPool createTransactionPool(int maxCapacity, SpanAwareTracer tracer) {
transactionPool = (BookkeeperObjectPool) super.createTransactionPool(maxCapacity, tracer);
return transactionPool;
}
@Override
- public ObjectPool createSpanPool(int maxCapacity, ElasticApmTracer tracer) {
+ public ObjectPool createSpanPool(int maxCapacity, SpanAwareTracer tracer) {
spanPool = (BookkeeperObjectPool) super.createSpanPool(maxCapacity, tracer);
return spanPool;
}
@Override
- public ObjectPool createErrorPool(int maxCapacity, ElasticApmTracer tracer) {
+ public ObjectPool createErrorPool(int maxCapacity, SpanAwareTracer tracer) {
errorPool = (BookkeeperObjectPool) super.createErrorPool(maxCapacity, tracer);
return errorPool;
}
@Override
- public ObjectPool createSpanLinkPool(int maxCapacity, ElasticApmTracer tracer) {
+ public ObjectPool createSpanLinkPool(int maxCapacity, SpanAwareTracer tracer) {
spanLinksPool = (BookkeeperObjectPool) super.createSpanLinkPool(maxCapacity, tracer);
return spanLinksPool;
}
diff --git a/apm-agent-plugins/apm-apache-httpclient/apm-apache-httpclient4-plugin/src/main/java/co/elastic/apm/agent/httpclient/v4/helper/ApacheHttpAsyncClientHelper.java b/apm-agent-plugins/apm-apache-httpclient/apm-apache-httpclient4-plugin/src/main/java/co/elastic/apm/agent/httpclient/v4/helper/ApacheHttpAsyncClientHelper.java
index f6f051fe2dd..39dd0b4b5cd 100644
--- a/apm-agent-plugins/apm-apache-httpclient/apm-apache-httpclient4-plugin/src/main/java/co/elastic/apm/agent/httpclient/v4/helper/ApacheHttpAsyncClientHelper.java
+++ b/apm-agent-plugins/apm-apache-httpclient/apm-apache-httpclient4-plugin/src/main/java/co/elastic/apm/agent/httpclient/v4/helper/ApacheHttpAsyncClientHelper.java
@@ -38,7 +38,7 @@ public class ApacheHttpAsyncClientHelper {
private final ObjectPool> futureCallbackWrapperObjectPool;
public ApacheHttpAsyncClientHelper() {
- ObjectPoolFactory factory = GlobalTracer.requireTracerImpl().getObjectPoolFactory();
+ ObjectPoolFactory factory = GlobalTracer.get().getObjectPoolFactory();
requestProducerWrapperObjectPool = factory.createRecyclableObjectPool(MAX_POOLED_ELEMENTS, new RequestProducerWrapperAllocator());
futureCallbackWrapperObjectPool = factory.createRecyclableObjectPool(MAX_POOLED_ELEMENTS, new FutureCallbackWrapperAllocator());
}
diff --git a/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/pluginapi/CaptureSpanInstrumentation.java b/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/pluginapi/CaptureSpanInstrumentation.java
index b3bc82c4ce7..acd6ad0e2c2 100644
--- a/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/pluginapi/CaptureSpanInstrumentation.java
+++ b/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/pluginapi/CaptureSpanInstrumentation.java
@@ -105,11 +105,6 @@ public static void onMethodExit(@Advice.Enter @Nullable Object span,
}
}
- @Override
- public ElementMatcher.Junction getClassLoaderMatcher() {
- return classLoaderCanLoadClass("co.elastic.apm.api.CaptureSpan");
- }
-
@Override
public ElementMatcher super TypeDescription> getTypeMatcher() {
return isInAnyPackage(stacktraceConfig.getApplicationPackages(), ElementMatchers.none())
diff --git a/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/pluginapi/TracedInstrumentation.java b/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/pluginapi/TracedInstrumentation.java
index 96b4a69fab1..03a822a18c4 100644
--- a/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/pluginapi/TracedInstrumentation.java
+++ b/apm-agent-plugins/apm-api-plugin/src/main/java/co/elastic/apm/agent/pluginapi/TracedInstrumentation.java
@@ -22,7 +22,7 @@
import co.elastic.apm.agent.bci.bytebuddy.AnnotationValueOffsetMappingFactory;
import co.elastic.apm.agent.bci.bytebuddy.SimpleMethodSignatureOffsetMappingFactory;
import co.elastic.apm.agent.configuration.CoreConfiguration;
-import co.elastic.apm.agent.impl.ElasticApmTracer;
+import co.elastic.apm.agent.impl.Tracer;
import co.elastic.apm.agent.impl.stacktrace.StacktraceConfiguration;
import co.elastic.apm.agent.impl.transaction.AbstractSpan;
import co.elastic.apm.agent.impl.transaction.Outcome;
@@ -40,25 +40,19 @@
import java.util.Arrays;
import java.util.Collection;
-import static co.elastic.apm.agent.bci.bytebuddy.CustomElementMatchers.classLoaderCanLoadClass;
-import static co.elastic.apm.agent.bci.bytebuddy.CustomElementMatchers.isInAnyPackage;
-import static co.elastic.apm.agent.bci.bytebuddy.CustomElementMatchers.isProxy;
-import static co.elastic.apm.agent.bci.bytebuddy.CustomElementMatchers.overridesOrImplementsMethodThat;
+import static co.elastic.apm.agent.bci.bytebuddy.CustomElementMatchers.*;
import static co.elastic.apm.agent.impl.transaction.AbstractSpan.PRIO_METHOD_SIGNATURE;
import static co.elastic.apm.agent.impl.transaction.AbstractSpan.PRIO_USER_SUPPLIED;
import static co.elastic.apm.agent.pluginapi.ElasticApmApiInstrumentation.PUBLIC_API_INSTRUMENTATION_GROUP;
import static co.elastic.apm.agent.pluginapi.Utils.FRAMEWORK_NAME;
-import static net.bytebuddy.matcher.ElementMatchers.declaresMethod;
-import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith;
-import static net.bytebuddy.matcher.ElementMatchers.named;
-import static net.bytebuddy.matcher.ElementMatchers.not;
+import static net.bytebuddy.matcher.ElementMatchers.*;
public class TracedInstrumentation extends TracerAwareInstrumentation {
private final CoreConfiguration coreConfig;
private final StacktraceConfiguration stacktraceConfig;
- public TracedInstrumentation(ElasticApmTracer tracer) {
+ public TracedInstrumentation(Tracer tracer) {
coreConfig = tracer.getConfig(CoreConfiguration.class);
stacktraceConfig = tracer.getConfig(StacktraceConfiguration.class);
}
diff --git a/apm-agent-plugins/apm-api-plugin/src/test/java/co/elastic/apm/api/AnnotationInheritanceTest.java b/apm-agent-plugins/apm-api-plugin/src/test/java/co/elastic/apm/api/AnnotationInheritanceTest.java
index 61cb5084c41..75049c35b19 100644
--- a/apm-agent-plugins/apm-api-plugin/src/test/java/co/elastic/apm/api/AnnotationInheritanceTest.java
+++ b/apm-agent-plugins/apm-api-plugin/src/test/java/co/elastic/apm/api/AnnotationInheritanceTest.java
@@ -23,6 +23,7 @@
import co.elastic.apm.agent.bci.ElasticApmAgent;
import co.elastic.apm.agent.configuration.CoreConfiguration;
import co.elastic.apm.agent.impl.ElasticApmTracer;
+import co.elastic.apm.agent.impl.Tracer;
import net.bytebuddy.agent.ByteBuddyAgent;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
diff --git a/apm-agent-plugins/apm-asynchttpclient-plugin/src/main/java/co/elastic/apm/agent/asynchttpclient/AbstractAsyncHttpClientInstrumentation.java b/apm-agent-plugins/apm-asynchttpclient-plugin/src/main/java/co/elastic/apm/agent/asynchttpclient/AbstractAsyncHttpClientInstrumentation.java
index 170267d3dfc..bd7ca07b3d3 100644
--- a/apm-agent-plugins/apm-asynchttpclient-plugin/src/main/java/co/elastic/apm/agent/asynchttpclient/AbstractAsyncHttpClientInstrumentation.java
+++ b/apm-agent-plugins/apm-asynchttpclient-plugin/src/main/java/co/elastic/apm/agent/asynchttpclient/AbstractAsyncHttpClientInstrumentation.java
@@ -21,7 +21,7 @@
import co.elastic.apm.agent.bci.TracerAwareInstrumentation;
import co.elastic.apm.agent.collections.WeakConcurrentProviderImpl;
import co.elastic.apm.agent.httpclient.HttpClientHelper;
-import co.elastic.apm.agent.impl.ElasticApmTracer;
+import co.elastic.apm.agent.impl.Tracer;
import co.elastic.apm.agent.impl.transaction.AbstractSpan;
import co.elastic.apm.agent.impl.transaction.Outcome;
import co.elastic.apm.agent.impl.transaction.Span;
@@ -174,7 +174,7 @@ public ElementMatcher super MethodDescription> getMethodMatcher() {
public static class AsyncHandlerOnCompletedInstrumentation extends AbstractAsyncHandlerInstrumentation {
- public AsyncHandlerOnCompletedInstrumentation(ElasticApmTracer tracer) {
+ public AsyncHandlerOnCompletedInstrumentation(Tracer tracer) {
super(named("onCompleted").and(takesArguments(0)));
}
@@ -198,7 +198,7 @@ public static void onMethodExit(@Nullable @Advice.Enter Object spanObj) {
public static class AsyncHandlerOnThrowableInstrumentation extends AbstractAsyncHandlerInstrumentation {
- public AsyncHandlerOnThrowableInstrumentation(ElasticApmTracer tracer) {
+ public AsyncHandlerOnThrowableInstrumentation(Tracer tracer) {
super(named("onThrowable").and(takesArguments(Throwable.class)));
}
@@ -225,7 +225,7 @@ public static void onMethodExit(@Nullable @Advice.Enter Object spanObj, @Advice.
public static class AsyncHandlerOnStatusReceivedInstrumentation extends AbstractAsyncHandlerInstrumentation {
- public AsyncHandlerOnStatusReceivedInstrumentation(ElasticApmTracer tracer) {
+ public AsyncHandlerOnStatusReceivedInstrumentation(Tracer tracer) {
super(named("onStatusReceived").and(takesArgument(0, named("org.asynchttpclient.HttpResponseStatus"))));
}
@@ -249,7 +249,7 @@ public static void onMethodExit(@Nullable @Advice.Enter Object spanObj, @Advice.
public static class StreamedAsyncHandlerOnStreamInstrumentation extends AbstractAsyncHandlerInstrumentation {
- public StreamedAsyncHandlerOnStreamInstrumentation(ElasticApmTracer tracer) {
+ public StreamedAsyncHandlerOnStreamInstrumentation(Tracer tracer) {
super(named("onStream").and(takesArgument(0, named("org.reactivestreams.Publisher"))));
}
diff --git a/apm-agent-plugins/apm-aws-sdk/apm-aws-sdk-1-plugin/src/main/java/co/elastic/apm/agent/awssdk/v1/helper/DynamoDbHelper.java b/apm-agent-plugins/apm-aws-sdk/apm-aws-sdk-1-plugin/src/main/java/co/elastic/apm/agent/awssdk/v1/helper/DynamoDbHelper.java
index ba6f86bfa8f..d7fa48bd2f8 100644
--- a/apm-agent-plugins/apm-aws-sdk/apm-aws-sdk-1-plugin/src/main/java/co/elastic/apm/agent/awssdk/v1/helper/DynamoDbHelper.java
+++ b/apm-agent-plugins/apm-aws-sdk/apm-aws-sdk-1-plugin/src/main/java/co/elastic/apm/agent/awssdk/v1/helper/DynamoDbHelper.java
@@ -19,8 +19,8 @@
package co.elastic.apm.agent.awssdk.v1.helper;
import co.elastic.apm.agent.awssdk.common.AbstractDynamoDBInstrumentationHelper;
-import co.elastic.apm.agent.impl.ElasticApmTracer;
import co.elastic.apm.agent.impl.GlobalTracer;
+import co.elastic.apm.agent.impl.Tracer;
import co.elastic.apm.agent.impl.transaction.Span;
import com.amazonaws.AmazonWebServiceRequest;
import com.amazonaws.Request;
@@ -31,13 +31,13 @@
public class DynamoDbHelper extends AbstractDynamoDBInstrumentationHelper, ExecutionContext> {
- private static final DynamoDbHelper INSTANCE = new DynamoDbHelper(GlobalTracer.requireTracerImpl());
+ private static final DynamoDbHelper INSTANCE = new DynamoDbHelper(GlobalTracer.get());
public static DynamoDbHelper getInstance() {
return INSTANCE;
}
- public DynamoDbHelper(ElasticApmTracer tracer) {
+ public DynamoDbHelper(Tracer tracer) {
super(tracer, SdkV1DataSource.getInstance());
}
diff --git a/apm-agent-plugins/apm-aws-sdk/apm-aws-sdk-1-plugin/src/main/java/co/elastic/apm/agent/awssdk/v1/helper/S3Helper.java b/apm-agent-plugins/apm-aws-sdk/apm-aws-sdk-1-plugin/src/main/java/co/elastic/apm/agent/awssdk/v1/helper/S3Helper.java
index 178c09ab917..c44d5a8968d 100644
--- a/apm-agent-plugins/apm-aws-sdk/apm-aws-sdk-1-plugin/src/main/java/co/elastic/apm/agent/awssdk/v1/helper/S3Helper.java
+++ b/apm-agent-plugins/apm-aws-sdk/apm-aws-sdk-1-plugin/src/main/java/co/elastic/apm/agent/awssdk/v1/helper/S3Helper.java
@@ -19,8 +19,8 @@
package co.elastic.apm.agent.awssdk.v1.helper;
import co.elastic.apm.agent.awssdk.common.AbstractS3InstrumentationHelper;
-import co.elastic.apm.agent.impl.ElasticApmTracer;
import co.elastic.apm.agent.impl.GlobalTracer;
+import co.elastic.apm.agent.impl.Tracer;
import com.amazonaws.Request;
import com.amazonaws.http.ExecutionContext;
@@ -28,13 +28,13 @@
public class S3Helper extends AbstractS3InstrumentationHelper, ExecutionContext> {
- private static final S3Helper INSTANCE = new S3Helper(GlobalTracer.requireTracerImpl());
+ private static final S3Helper INSTANCE = new S3Helper(GlobalTracer.get());
public static S3Helper getInstance() {
return INSTANCE;
}
- public S3Helper(ElasticApmTracer tracer) {
+ public S3Helper(Tracer tracer) {
super(tracer, SdkV1DataSource.getInstance());
}
}
diff --git a/apm-agent-plugins/apm-aws-sdk/apm-aws-sdk-1-plugin/src/main/java/co/elastic/apm/agent/awssdk/v1/helper/SQSHelper.java b/apm-agent-plugins/apm-aws-sdk/apm-aws-sdk-1-plugin/src/main/java/co/elastic/apm/agent/awssdk/v1/helper/SQSHelper.java
index 4abd688d61f..53856d4817f 100644
--- a/apm-agent-plugins/apm-aws-sdk/apm-aws-sdk-1-plugin/src/main/java/co/elastic/apm/agent/awssdk/v1/helper/SQSHelper.java
+++ b/apm-agent-plugins/apm-aws-sdk/apm-aws-sdk-1-plugin/src/main/java/co/elastic/apm/agent/awssdk/v1/helper/SQSHelper.java
@@ -21,8 +21,8 @@
import co.elastic.apm.agent.awssdk.common.AbstractSQSInstrumentationHelper;
import co.elastic.apm.agent.awssdk.v1.helper.sqs.wrapper.ReceiveMessageResultWrapper;
import co.elastic.apm.agent.configuration.CoreConfiguration;
-import co.elastic.apm.agent.impl.ElasticApmTracer;
import co.elastic.apm.agent.impl.GlobalTracer;
+import co.elastic.apm.agent.impl.Tracer;
import co.elastic.apm.agent.impl.transaction.Span;
import co.elastic.apm.agent.impl.transaction.TextHeaderSetter;
import co.elastic.apm.agent.impl.transaction.TraceContext;
@@ -46,13 +46,13 @@
public class SQSHelper extends AbstractSQSInstrumentationHelper, ExecutionContext, Message> implements TextHeaderSetter