diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 20d950ad14..ed60b962ff 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -33,6 +33,7 @@ updates: - dependency-name: "com.datastax.oss:java-driver-core" - dependency-name: "io.micrometer:*" - dependency-name: "jakarta.*:*" + - dependency-name: "io.opentelemetry:opentelemetry-api" ignore: - dependency-name: "net.bytebuddy:byte-buddy-agent" # We deliberately want to keep this older version of Byte Buddy for our runtime attach test diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 88832e5213..bc4810a8e7 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -50,6 +50,7 @@ option. If there's a need to disable the log correlation mechanism, this can be {pull}2428[#2428] * Added automatic error event capturing for log4j1 and JBoss LogManager - {pull}2428[#2428] * Issue a warning when security manager is mis-configured - {pull}2510[#2510] +* Add experimental OpenTelemetry API bridge - {pull}1631[#1631] [float] ===== Performance improvements @@ -151,7 +152,7 @@ paths. The BCI warmup is on by default and may be disabled through the internal ==== 1.28.1 - 2021/12/10 [float] -===== Security +===== Security * Fix for "Log4Shell" RCE 0-day exploit in log4j https://nvd.nist.gov/vuln/detail/CVE-2021-44228[CVE-2021-44228] - {pull}2332[#2332] [float] @@ -886,7 +887,7 @@ for more details) - {pull}1009[#1009] *** Rely on the existing `ELASTIC_APM_CAPTURE_BODY` agent config option (off by default). *** Send as `context.message.body` *** Limit size to 10000 characters. If longer than this size, trim to 9999 and append with ellipsis -** Introduce the `ignore_message_queues` configuration to disable instrumentation (message tagging) for specific +** Introduce the `ignore_message_queues` configuration to disable instrumentation (message tagging) for specific queues/topics as suggested in {pull}710[#710] ** Capture predefined message headers and all properties *** Rely on the existing `ELASTIC_APM_CAPTURE_HEADERS` agent config option. @@ -910,12 +911,12 @@ for more details) - {pull}1009[#1009] config option] * Add ability to ignore some exceptions to be reported as errors <> * Added <> configuration option * Improve servlet error capture {pull}812[#812] - Among others, now also takes Spring MVC `@ExceptionHandler`s into account + Among others, now also takes Spring MVC `@ExceptionHandler`s into account * Instrument Logger#error(String, Throwable) {pull}821[#821] Automatically captures exceptions when calling `logger.error("message", exception)` * Easier log correlation with https://github.com/elastic/java-ecs-logging. See <>. @@ -952,7 +953,7 @@ config option] * Error in log when setting <> to an empty string - `co.elastic.apm.agent.configuration.ApmServerConfigurationSource - Expected previousException not to be null` * Avoid terminating the TCP connection to APM Server when polling for configuration updates {pull}823[#823] - + [[release-notes-1.9.0]] ==== 1.9.0 - 2019/08/22 @@ -982,7 +983,7 @@ See https://discuss.elastic.co/t/elastic-apm-agent-jdbchelper-seems-to-use-a-lot [float] ==== Breaking Changes * The `apm-agent-attach.jar` is not executable anymore. -Use `apm-agent-attach-standalone.jar` instead. +Use `apm-agent-attach-standalone.jar` instead. [[release-notes-1.8.0]] ==== 1.8.0 - 2019/07/30 @@ -990,7 +991,7 @@ Use `apm-agent-attach-standalone.jar` instead. [float] ===== Features * Added support for tracking https://www.elastic.co/guide/en/kibana/7.3/transactions.html[time spent by span type]. - Can be disabled by setting https://www.elastic.co/guide/en/apm/agent/java/current/config-core.html#config-breakdown-metrics[`breakdown_metrics`] to `false`. + Can be disabled by setting https://www.elastic.co/guide/en/apm/agent/java/current/config-core.html#config-breakdown-metrics[`breakdown_metrics`] to `false`. * Added support for https://www.elastic.co/guide/en/kibana/7.3/agent-configuration.html[central configuration]. Can be disabled by setting <> to `false`. * Added support for Spring's JMS flavor - instrumenting `org.springframework.jms.listener.SessionAwareMessageListener` @@ -1036,7 +1037,7 @@ NOTE: Using wildcards is still not the recommended approach for the `trace_metho This requires APM Server 7.2+. * Added basic support for JMS- distributed tracing for basic scenarios of `send`, `receive`, `receiveNoWait` and `onMessage`. Both Queues and Topics are supported. -Async `send` APIs are not supported in this version. +Async `send` APIs are not supported in this version. NOTE: This feature is currently marked as "experimental" and is disabled by default. In order to enable, it is required to set the <> @@ -1076,11 +1077,11 @@ configuration property to an empty string. [float] ===== Related Announcements * Java APM Agent became part of the Cloud Foundry Java Buildpack as of https://github.com/cloudfoundry/java-buildpack/releases/tag/v4.19[Release v4.19] - + [float] ===== Features * Support Apache HttpAsyncClient - span creation and cross-service trace context propagation -* Added the `jvm.thread.count` metric, indicating the number of live threads in the JVM (daemon and non-daemon) +* Added the `jvm.thread.count` metric, indicating the number of live threads in the JVM (daemon and non-daemon) * Added support for WebLogic * Added support for Spring `@Scheduled` and EJB `@Schedule` annotations - https://github.com/elastic/apm-agent-java/pull/569[#569] @@ -1110,7 +1111,7 @@ This change also renames `tag` to `label` on the API level to be compliant with The `addTag(String, String)` method is still supported but deprecated in favor of `addLabel(String, String)`. As of version 7.x of the stack, labels will be stored under `labels` in Elasticsearch. Previously, they were stored under `context.tags`. -* Support async queries made by Elasticsearch REST client +* Support async queries made by Elasticsearch REST client * Added `setStartTimestamp(long epochMicros)` and `end(long epochMicros)` API methods to `Span` and `Transaction`, allowing to set custom start and end timestamps. * Auto-detection of the `service_name` based on the `` element of the `web.xml` with a fallback to the servlet context path. @@ -1148,14 +1149,14 @@ controls which `Content-Type`s should be captured. * Introduces a new configuration option `disable_metrics` which disables the collection of metrics via a wildcard expression. * Support for HttpUrlConnection * Adds `subtype` and `action` to spans. This replaces former typing mechanism where type, subtype and action were all set through - the type in an hierarchical dotted-syntax. In order to support existing API usages, dotted types are parsed into subtype and action, - however `Span.createSpan` and `Span.setType` are deprecated starting this version. Instead, type-less spans can be created using the new + the type in an hierarchical dotted-syntax. In order to support existing API usages, dotted types are parsed into subtype and action, + however `Span.createSpan` and `Span.setType` are deprecated starting this version. Instead, type-less spans can be created using the new `Span.startSpan` API and typed spans can be created using the new `Span.startSpan(String type, String subtype, String action)` API * Support for JBoss EAP 6.4, 7.0, 7.1 and 7.2 * Improved startup times * Support for SOAP (JAX-WS). SOAP client create spans and propagate context. - Transactions are created for `@WebService` classes and `@WebMethod` methods. + Transactions are created for `@WebService` classes and `@WebMethod` methods. [float] ===== Bug Fixes @@ -1188,7 +1189,7 @@ controls which `Content-Type`s should be captured. * Fixing a potential memory leak when there is no connection with APM server * Fixes NoSuchMethodError CharBuffer.flip() which occurs when using the Elasticsearch RestClient and Java 7 or 8 https://github.com/elastic/apm-agent-java/pull/401[#401] - + [[release-notes-1.2.0]] ==== 1.2.0 - 2018/12/19 @@ -1197,7 +1198,7 @@ controls which `Content-Type`s should be captured. * Added `capture_headers` configuration option. Set to `false` to disable capturing request and response headers. This will reduce the allocation rate of the agent and can save you network bandwidth and disk space. -* Makes the API methods `addTag`, `setName`, `setType`, `setUser` and `setResult` fluent, so that calls can be chained. +* Makes the API methods `addTag`, `setName`, `setType`, `setUser` and `setResult` fluent, so that calls can be chained. [float] ===== Bug Fixes @@ -1217,7 +1218,7 @@ controls which `Content-Type`s should be captured. [float] ===== Bug Fixes * Update dsl-json which fixes a memory leak. - See https://github.com/ngs-doo/dsl-json/pull/102[ngs-doo/dsl-json#102] for details. + See https://github.com/ngs-doo/dsl-json/pull/102[ngs-doo/dsl-json#102] for details. * Avoid `VerifyError`s by non instrumenting classes compiled for Java 4 or earlier * Enable APM Server URL configuration with path (fixes #339) * Reverse `system.hostname` and `system.platform` order sent to APM server @@ -1237,7 +1238,7 @@ controls which `Content-Type`s should be captured. * Remove intake v1 support. This version requires APM Server 6.5.0+ which supports the intake api v2. Until the time the APM Server 6.5.0 is officially released, you can test with docker by pulling the APM Server image via - `docker pull docker.elastic.co/apm/apm-server:6.5.0-SNAPSHOT`. + `docker pull docker.elastic.co/apm/apm-server:6.5.0-SNAPSHOT`. [float] ===== Features @@ -1332,7 +1333,7 @@ Transactions are named based on your resources (`ResourceClass#resourceMethod`). * Public API ** Add `Span#captureException` and `Transaction#captureException` to public API. `ElasticApm.captureException` is deprecated now. Use `ElasticApm.currentSpan().captureException(exception)` instead. -** Added `Transaction.getId` and `Span.getId` methods +** Added `Transaction.getId` and `Span.getId` methods * Added support for async servlet requests * Added support for Payara/Glassfish * Incubating support for Apache HttpClient @@ -1342,7 +1343,7 @@ Transactions are named based on your resources (`ResourceClass#resourceMethod`). As that could contain path parameters, like `/user/$userId` however, You can set the `url_groups` option to define a wildcard pattern, like `/user/*`, to group those paths together. - This is especially helpful when using an unsupported Servlet API-based framework. + This is especially helpful when using an unsupported Servlet API-based framework. * Support duration suffixes (`ms`, `s` and `m`) for duration configuration options. Not using the duration suffix logs out a deprecation warning and will not be supported in future versions. * Add ability to add multiple APM server URLs, which enables client-side load balancing. diff --git a/apm-agent-api/pom.xml b/apm-agent-api/pom.xml index 58e0e60aeb..ef4a956faa 100644 --- a/apm-agent-api/pom.xml +++ b/apm-agent-api/pom.xml @@ -5,7 +5,7 @@ apm-agent-parent co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-agent-api diff --git a/apm-agent-attach-cli/pom.xml b/apm-agent-attach-cli/pom.xml index de61597487..137dde5893 100644 --- a/apm-agent-attach-cli/pom.xml +++ b/apm-agent-attach-cli/pom.xml @@ -3,7 +3,7 @@ apm-agent-parent co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/apm-agent-attach/pom.xml b/apm-agent-attach/pom.xml index 882f786713..517a323f2e 100644 --- a/apm-agent-attach/pom.xml +++ b/apm-agent-attach/pom.xml @@ -5,7 +5,7 @@ apm-agent-parent co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-agent-attach diff --git a/apm-agent-benchmarks/pom.xml b/apm-agent-benchmarks/pom.xml index 99d049a3e0..9d3e39ef53 100644 --- a/apm-agent-benchmarks/pom.xml +++ b/apm-agent-benchmarks/pom.xml @@ -5,7 +5,7 @@ apm-agent-parent co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-agent-benchmarks diff --git a/apm-agent-bootstrap/pom.xml b/apm-agent-bootstrap/pom.xml index 94a130b166..49e32e1af0 100644 --- a/apm-agent-bootstrap/pom.xml +++ b/apm-agent-bootstrap/pom.xml @@ -5,7 +5,7 @@ apm-agent-parent co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-agent-bootstrap diff --git a/apm-agent-cached-lookup-key/pom.xml b/apm-agent-cached-lookup-key/pom.xml index 3d087265f3..2f4efbd6ae 100644 --- a/apm-agent-cached-lookup-key/pom.xml +++ b/apm-agent-cached-lookup-key/pom.xml @@ -3,7 +3,7 @@ apm-agent-parent co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/apm-agent-common/pom.xml b/apm-agent-common/pom.xml index 19c0ea89fc..692d536e77 100644 --- a/apm-agent-common/pom.xml +++ b/apm-agent-common/pom.xml @@ -3,7 +3,7 @@ apm-agent-parent co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/apm-agent-core/pom.xml b/apm-agent-core/pom.xml index 7cd5a1f435..9aed6aa5b2 100644 --- a/apm-agent-core/pom.xml +++ b/apm-agent-core/pom.xml @@ -5,7 +5,7 @@ apm-agent-parent co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-agent-core 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 9be39a785b..0753f3e9b7 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 @@ -31,6 +31,7 @@ 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; @@ -85,13 +86,13 @@ public class ElasticApmTracer implements Tracer { private final ObjectPool errorPool; private final Reporter reporter; private final ObjectPoolFactory objectPoolFactory; - // Maintains a stack of all the activated spans + // Maintains a stack of all the activated spans/contexts // This way its easy to retrieve the bottom of the stack (the transaction) // Also, the caller does not have to keep a reference to the previously active span, as that is maintained by the stack - private final ThreadLocal>> activeStack = new ThreadLocal>>() { + private final ThreadLocal>> activeStack = new ThreadLocal>>() { @Override - protected Deque> initialValue() { - return new ArrayDeque>(); + protected Deque> initialValue() { + return new ArrayDeque>(); } }; @@ -256,7 +257,7 @@ private Transaction createTransaction() { @Override @Nullable public Transaction currentTransaction() { - final AbstractSpan bottomOfStack = activeStack.get().peekLast(); + final ElasticContext bottomOfStack = activeStack.get().peekLast(); return bottomOfStack != null ? bottomOfStack.getTransaction() : null; } @@ -494,6 +495,12 @@ public ObjectPoolFactory getObjectPoolFactory() { @Override @Nullable public AbstractSpan getActive() { + ElasticContext active = activeStack.get().peek(); + return active != null ? active.getSpan() : null; + } + + @Nullable + public ElasticContext getActiveContext() { return activeStack.get().peek(); } @@ -698,54 +705,105 @@ public T getLifecycleListener(Class listenerClass) { return null; } - public void activate(AbstractSpan span) { + @Nullable + public ElasticContext currentContext() { + return activeStack.get().peek(); + } + + public void activate(ElasticContext context) { if (logger.isDebugEnabled()) { - logger.debug("Activating {} on thread {}", span, Thread.currentThread().getId()); - } - span.incrementReferences(); - List activationListeners = getActivationListeners(); - for (int i = 0, size = activationListeners.size(); i < size; i++) { - try { - activationListeners.get(i).beforeActivate(span); - } catch (Error e) { - throw e; - } catch (Throwable t) { - logger.warn("Exception while calling {}#beforeActivate", activationListeners.get(i).getClass().getSimpleName(), t); + logger.debug("Activating {} on thread {}", context, Thread.currentThread().getId()); + } + + ElasticContext currentContext = currentContext(); + ElasticContext newContext = context; + + AbstractSpan span = context.getSpan(); + if (span != null) { + span.incrementReferences(); + triggerActivationListeners(span, true); + } else if(currentContext != null) { + // when there is no span attached to the context we are attaching to but there is one in the current + // context, we just propagate to the context that will be activated. + span = currentContext.getSpan(); + if (span != null) { + newContext = context.withActiveSpan(span); } } - activeStack.get().push(span); + + activeStack.get().push(newContext); + } + + public Scope activateInScope(final ElasticContext context) { + // already in scope + if (getActiveContext() == 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(); + } + }; } - public void deactivate(AbstractSpan span) { + public void deactivate(ElasticContext context) { if (logger.isDebugEnabled()) { - logger.debug("Deactivating {} on thread {}", span, Thread.currentThread().getId()); + logger.debug("Deactivating {} on thread {}", context, Thread.currentThread().getId()); } + ElasticContext activeContext = activeStack.get().poll(); + AbstractSpan span = context.getSpan(); + + if (activeContext != context && context == span) { + // when context has been upgraded, we need to deactivate the original span + activeContext = context; + } + try { - final Deque> stack = activeStack.get(); - assertIsActive(span, stack.poll()); - List activationListeners = getActivationListeners(); - for (int i = 0, size = activationListeners.size(); i < size; i++) { - try { - // `this` is guaranteed to not be recycled yet as the reference count is only decremented after this method has executed - activationListeners.get(i).afterDeactivate(span); - } catch (Error e) { - throw e; - } catch (Throwable t) { - logger.warn("Exception while calling {}#afterDeactivate", activationListeners.get(i).getClass().getSimpleName(), t); - } + assertIsActive(context, activeContext); + + if (null != span) { + triggerActivationListeners(span, false); } } finally { - span.decrementReferences(); + if (null != span) { + span.decrementReferences(); + } } } - private void assertIsActive(AbstractSpan span, @Nullable AbstractSpan currentlyActive) { - if (span != currentlyActive) { - logger.warn("Deactivating a span ({}) which is not the currently active span ({}). " + - "This can happen when not properly deactivating a previous span.", span, currentlyActive); + private void assertIsActive(ElasticContext context, @Nullable ElasticContext currentlyActive) { + if (context != currentlyActive) { + logger.warn("Deactivating a context ({}) which is not the currently active one ({}). " + + "This can happen when not properly deactivating a previous span or context.", context, currentlyActive); if (assertionsEnabled) { - throw new AssertionError("Deactivating a span that is not the active one"); + throw new AssertionError("Deactivating a context that is not the active one"); + } + } + } + + private void triggerActivationListeners(AbstractSpan span, boolean isActivate) { + List activationListeners = getActivationListeners(); + for (int i = 0, size = activationListeners.size(); i < size; i++) { + ActivationListener listener = activationListeners.get(i); + try { + if (isActivate) { + listener.beforeActivate(span); + } else { + // `this` is guaranteed to not be recycled yet as the reference count is only decremented after this method has executed + listener.afterDeactivate(span); + } + } catch (Error e) { + throw e; + } catch (Throwable t) { + logger.warn("Exception while calling {}#{}", listener.getClass().getSimpleName(), isActivate ? "beforeActivate" : "afterDeactivate", t); } } } diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/context/Url.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/context/Url.java index 5a0b29b889..7b82da35e4 100644 --- a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/context/Url.java +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/context/Url.java @@ -89,8 +89,6 @@ public StringBuilder getFull() { /** * Updates full URL from current state of {@literal this}. Must be called after all other Url fields are set. - * - * @return url */ private void updateFull() { // inspired by org.apache.catalina.connector.Request.getRequestURL @@ -262,7 +260,7 @@ public void parseAndFillFromFull() { } } - private static int normalizePort(int port, @Nullable String protocol) { + public static int normalizePort(int port, @Nullable String protocol) { int portValue = port; if (portValue < 0 && protocol != null) { // Work around java.net.URL bug 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 8c08b26bee..0ab3d9b9b2 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 @@ -30,12 +30,14 @@ import co.elastic.apm.agent.sdk.logging.LoggerFactory; import javax.annotation.Nullable; +import java.util.HashMap; +import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; -public abstract class AbstractSpan> implements Recyclable { +public abstract class AbstractSpan> implements Recyclable, ElasticContext { public static final int PRIO_USER_SUPPLIED = 1000; public static final int PRIO_HIGH_LEVEL_FRAMEWORK = 100; public static final int PRIO_METHOD_SIGNATURE = 100; @@ -107,6 +109,11 @@ public abstract class AbstractSpan> implements Recycla protected final AtomicReference bufferedSpan = new AtomicReference<>(); + @Nullable + private OTelSpanKind otelKind = null; + + private final Map otelAttributes = new HashMap<>(); + public int getReferenceCount() { return references.get(); } @@ -135,9 +142,6 @@ public boolean isDiscarded() { return discardRequested && getTraceContext().isDiscardable(); } - @Nullable - public abstract Transaction getTransaction(); - private static class ChildDurationTimer implements Recyclable { private AtomicInteger activeChildren = new AtomicInteger(); @@ -359,6 +363,8 @@ public void resetState() { userOutcome = null; hasCapturedExceptions = false; bufferedSpan.set(null); + otelKind = null; + otelAttributes.clear(); } public Span createSpan() { @@ -503,28 +509,21 @@ private boolean hasChildId(Id spanId) { return false; } + @Override public T activate() { tracer.activate(this); return (T) this; } + @Override public T deactivate() { tracer.deactivate(this); return (T) this; } + @Override public Scope activateInScope() { - // already in scope - if (tracer.getActive() == this) { - return Scope.NoopScope.INSTANCE; - } - activate(); - return new Scope() { - @Override - public void close() { - deactivate(); - } - }; + return tracer.activateInScope(this); } /** @@ -675,4 +674,30 @@ public T withUserOutcome(Outcome outcome) { return thiz(); } + @Override + public ElasticContext withActiveSpan(AbstractSpan span) { + // for internal spans the active span is only stored implicitly in the stack, hence we have no requirement + // to have any other kind of context storage. + return this; + } + + @Override + public AbstractSpan getSpan() { + return this; + } + + public T withOtelKind(OTelSpanKind kind) { + this.otelKind = kind; + return thiz(); + } + + @Nullable + public OTelSpanKind getOtelKind() { + return otelKind; + } + + public Map getOtelAttributes() { + return otelAttributes; + } + } diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/ElasticContext.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/ElasticContext.java new file mode 100644 index 0000000000..4aa2cbb228 --- /dev/null +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/ElasticContext.java @@ -0,0 +1,68 @@ +/* + * 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.Scope; + +import javax.annotation.Nullable; + +public interface ElasticContext> { + + /** + * Makes the context active + * + * @return this + */ + T activate(); + + /** + * Deactivates context + * + * @return this + */ + T deactivate(); + + /** + * Activates context in a scope + * + * @return active scope that will deactivate context when closed + */ + Scope activateInScope(); + + /** + * Adds a span as active within context. Might return a different context instance if required, for example + * when the context implementation is immutable and thus can't be modified. + * + * @param span span to add to the context + * @return context with activated span + */ + ElasticContext withActiveSpan(AbstractSpan span); + + /** + * @return the span/transaction that is associated to this context, {@literal null} if there is none + */ + @Nullable + AbstractSpan getSpan(); + + /** + * @return transaction associated to this context, {@literal null} if there is none + */ + @Nullable + Transaction getTransaction(); +} diff --git a/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/OTelSpanKind.java b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/OTelSpanKind.java new file mode 100644 index 0000000000..1193891586 --- /dev/null +++ b/apm-agent-core/src/main/java/co/elastic/apm/agent/impl/transaction/OTelSpanKind.java @@ -0,0 +1,30 @@ +/* + * 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; + +/** + * Mirrors OpenTelemetry span kind + */ +public enum OTelSpanKind { + INTERNAL, + SERVER, + CLIENT, + PRODUCER, + CONSUMER +} 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 7c3c894357..6ec01f8fce 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 @@ -52,6 +52,7 @@ import co.elastic.apm.agent.impl.transaction.Faas; import co.elastic.apm.agent.impl.transaction.FaasTrigger; import co.elastic.apm.agent.impl.transaction.Id; +import co.elastic.apm.agent.impl.transaction.OTelSpanKind; import co.elastic.apm.agent.impl.transaction.Span; import co.elastic.apm.agent.impl.transaction.SpanCount; import co.elastic.apm.agent.impl.transaction.StackFrame; @@ -713,6 +714,7 @@ private void serializeSpan(final Span span) { if (!Double.isNaN(sampleRate)) { writeField("sample_rate", sampleRate); } + serializeOTel(span); if (span.isComposite()) { serializeComposite(span.getComposite()); } @@ -720,6 +722,61 @@ private void serializeSpan(final Span span) { jw.writeByte(OBJECT_END); } + private void serializeOTel(Span span) { + OTelSpanKind kind = span.getOtelKind(); + Map attributes = span.getOtelAttributes(); + boolean hasAttributes = !attributes.isEmpty(); + boolean hasKind = kind != null; + if (hasKind || hasAttributes) { + writeFieldName("otel"); + jw.writeByte(OBJECT_START); + + if (hasKind) { + writeFieldName("span_kind"); + writeStringValue(kind.name()); + } + + if (hasAttributes) { + if (hasKind) { + jw.writeByte(COMMA); + } + writeFieldName("attributes"); + jw.writeByte(OBJECT_START); + int index = 0; + for (Map.Entry entry : attributes.entrySet()) { + if (index++ > 0) { + jw.writeByte(COMMA); + } + writeFieldName(entry.getKey()); + Object o = entry.getValue(); + if (o instanceof Number) { + serializeNumber((Number) o, jw); + } else if (o instanceof String) { + writeStringValue((String) o); + } else if (o instanceof Boolean) { + BoolConverter.serialize((Boolean) o, jw); + } + } + jw.writeByte(OBJECT_END); + } + + jw.writeByte(OBJECT_END); + jw.writeByte(COMMA); + } + } + + private void serializeNumber(Number n, JsonWriter jw) { + if (n instanceof Integer) { + NumberConverter.serialize(n.intValue(), jw); + } else if (n instanceof Long) { + NumberConverter.serialize(n.longValue(), jw); + } else if (n instanceof Double) { + NumberConverter.serialize(n.doubleValue(), jw); + } else if (n instanceof Float) { + NumberConverter.serialize(n.floatValue(), jw); + } + } + private void serializeComposite(Composite composite) { writeFieldName("composite", jw); jw.writeByte(OBJECT_START); @@ -1387,7 +1444,7 @@ private void writeField(final String fieldName, final PotentiallyMultiValuedMap writeFieldName(fieldName); jw.writeByte(OBJECT_START); int size = map.size(); - if(supportsMultipleValues){ + if (supportsMultipleValues) { serializePotentiallyMultiValuedEntry(map.getKey(0), map.getValue(0)); for (int i = 1; i < size; i++) { jw.writeByte(COMMA); diff --git a/apm-agent-core/src/test/java/co/elastic/apm/agent/report/serialize/DslJsonSerializerTest.java b/apm-agent-core/src/test/java/co/elastic/apm/agent/report/serialize/DslJsonSerializerTest.java index 10a0b4b764..22551b3531 100644 --- a/apm-agent-core/src/test/java/co/elastic/apm/agent/report/serialize/DslJsonSerializerTest.java +++ b/apm-agent-core/src/test/java/co/elastic/apm/agent/report/serialize/DslJsonSerializerTest.java @@ -46,6 +46,7 @@ import co.elastic.apm.agent.impl.sampling.Sampler; import co.elastic.apm.agent.impl.stacktrace.StacktraceConfiguration; import co.elastic.apm.agent.impl.transaction.Id; +import co.elastic.apm.agent.impl.transaction.OTelSpanKind; import co.elastic.apm.agent.impl.transaction.Span; import co.elastic.apm.agent.impl.transaction.StackFrame; import co.elastic.apm.agent.impl.transaction.TraceContext; @@ -1342,6 +1343,43 @@ void multiValueHeaders(boolean supportsMulti) { assertThat(headersJson).isNotNull(); } + @Test + void testOTelSpanSerialization() { + Span span = new Span(MockTracer.create()).withName("span name"); + assertThat(span.getOtelKind()) + .describedAs("otel span kind should not be set by default") + .isNull(); + + JsonNode spanJson = readJsonString(serializer.toJsonString(span)); + assertThat(spanJson.get("name").asText()).isEqualTo("span name"); + assertThat(spanJson.get("otel")).isNull(); + + for (OTelSpanKind kind : OTelSpanKind.values()) { + span.withOtelKind(kind); + spanJson = readJsonString(serializer.toJsonString(span)); + + JsonNode otelJson = spanJson.get("otel"); + assertThat(otelJson).isNotNull(); + assertThat(otelJson.get("span_kind").asText()).isEqualTo(kind.name()); + } + + // with custom otel attributes + span.getOtelAttributes().put("attribute.string", "hello"); + span.getOtelAttributes().put("attribute.long", 123L); + span.getOtelAttributes().put("attribute.boolean", false); + span.getOtelAttributes().put("attribute.float", 0.42f); + spanJson = readJsonString(serializer.toJsonString(span)); + JsonNode otelJson = spanJson.get("otel"); + assertThat(otelJson).isNotNull(); + JsonNode otelAttributes = otelJson.get("attributes"); + assertThat(otelAttributes).isNotNull(); + assertThat(otelAttributes.size()).isEqualTo(4); + assertThat(otelAttributes.get("attribute.string").asText()).isEqualTo("hello"); + assertThat(otelAttributes.get("attribute.long").asLong()).isEqualTo(123L); + assertThat(otelAttributes.get("attribute.boolean").asBoolean()).isFalse(); + assertThat(otelAttributes.get("attribute.float").asDouble()).isEqualTo(0.42d); + } + @Test void testNonCompositeSpan() { Span span = new Span(MockTracer.create()); diff --git a/apm-agent-core/src/test/java/specs/BaseStepDefinitions.java b/apm-agent-core/src/test/java/specs/BaseStepDefinitions.java index b04ed36494..59ba642c52 100644 --- a/apm-agent-core/src/test/java/specs/BaseStepDefinitions.java +++ b/apm-agent-core/src/test/java/specs/BaseStepDefinitions.java @@ -18,6 +18,7 @@ */ package specs; +import io.cucumber.java.ParameterType; import io.cucumber.java.en.Given; import java.util.Collections; @@ -57,4 +58,9 @@ public void startSpan() { public void endContext(String context) { scenarioState.getContext(context).end(); } + + @ParameterType("transaction|span") + public String contextType(String contextType) { + return contextType; + } } diff --git a/apm-agent-core/src/test/java/specs/OutcomeStepsDefinitions.java b/apm-agent-core/src/test/java/specs/OutcomeStepsDefinitions.java index ab916bf6e0..f7fa511955 100644 --- a/apm-agent-core/src/test/java/specs/OutcomeStepsDefinitions.java +++ b/apm-agent-core/src/test/java/specs/OutcomeStepsDefinitions.java @@ -38,14 +38,14 @@ public OutcomeStepsDefinitions(ScenarioState state) { this.state = state; } - @Given("the agent sets the {} outcome to {string}") + @Given("the agent sets the {contextType} outcome to {string}") public void internalSetOutcome(String context, String outcome) { - setInternalOutcome(state.getContext(context), outcome); + setInternalOutcome(state.getContext(context), fromString(outcome)); } - @Given("a user sets the {} outcome to {string}") + @Given("a user sets the {contextType} outcome to {string}") public void userSetOutcome(String context, String outcome) { - setUserOutcome(state.getContext(context), outcome); + setUserOutcome(state.getContext(context), fromString(outcome)); } @Given("an error is reported to the {}") @@ -53,9 +53,9 @@ public void reportError(String context) { state.getContext(context).captureException(new Throwable()); } - @Then("the {} outcome is {string}") + @Then("the {contextType} outcome is {string}") public void thenOutcomeIs(String context, String outcome) { - checkOutcome(context.equals("span") ? state.getSpan() : state.getTransaction(), outcome); + checkOutcome(context.equals("span") ? state.getSpan() : state.getTransaction(), fromString(outcome)); } // HTTP spans & transactions mapping @@ -76,21 +76,21 @@ public void httpTransactionWithStatus(int code) { // utilities - static void setUserOutcome(AbstractSpan context, String outcome) { + static void setUserOutcome(AbstractSpan context, Outcome outcome) { assertThat(context).isNotNull(); - context.withUserOutcome(fromString(outcome)); + context.withUserOutcome(outcome); } - static void setInternalOutcome(AbstractSpan context, String outcome) { + static void setInternalOutcome(AbstractSpan context, Outcome outcome) { assertThat(context).isNotNull(); - context.withOutcome(fromString(outcome)); + context.withOutcome(outcome); } - static void checkOutcome(AbstractSpan context, String outcome) { + static void checkOutcome(AbstractSpan context, Outcome outcome) { assertThat(context).isNotNull(); assertThat(context.getOutcome()) .describedAs("expected outcome = %s for context = %s", outcome, context) - .isEqualTo(fromString(outcome)); + .isEqualTo(outcome); } static Outcome fromString(String outcome) { diff --git a/apm-agent-core/src/test/java/specs/RunCucumberTest.java b/apm-agent-core/src/test/java/specs/RunCucumberTest.java index cb7a6dac20..c82af05854 100644 --- a/apm-agent-core/src/test/java/specs/RunCucumberTest.java +++ b/apm-agent-core/src/test/java/specs/RunCucumberTest.java @@ -23,6 +23,6 @@ import org.junit.runner.RunWith; @RunWith(Cucumber.class) -@CucumberOptions(strict = true, plugin = {"pretty"}, tags = "not @grpc") +@CucumberOptions(strict = true, plugin = {"pretty"}, tags = "not @grpc and not @opentelemetry-bridge") public class RunCucumberTest { } diff --git a/apm-agent-core/src/test/resources/json-specs/span_types.json b/apm-agent-core/src/test/resources/json-specs/span_types.json index aa6e47f56a..d0626ca2ef 100644 --- a/apm-agent-core/src/test/resources/json-specs/span_types.json +++ b/apm-agent-core/src/test/resources/json-specs/span_types.json @@ -325,6 +325,10 @@ ], "allow_unlisted_subtype": true }, + "unknown": { + "__description": "For bridged spans that can't be mapped to known type/subtype", + "allow_null_subtype": true + }, "websocket": { "__description": "Websockets", "subtypes": { diff --git a/apm-agent-core/src/test/resources/specs/otel_bridge.feature b/apm-agent-core/src/test/resources/specs/otel_bridge.feature new file mode 100644 index 0000000000..5579e6ad42 --- /dev/null +++ b/apm-agent-core/src/test/resources/specs/otel_bridge.feature @@ -0,0 +1,244 @@ +@opentelemetry-bridge +Feature: OpenTelemetry bridge + + # --- Creating Elastic span or transaction from OTel span + + Scenario: Create transaction from OTel span with remote context + Given an agent + And OTel span is created with remote context as parent + Then Elastic bridged object is a transaction + Then Elastic bridged transaction has remote context as parent + + Scenario: Create root transaction from OTel span without parent + Given an agent + And OTel span is created without parent + And OTel span ends + Then Elastic bridged object is a transaction + Then Elastic bridged transaction is a root transaction + # outcome should not be inferred from the lack/presence of errors + Then Elastic bridged transaction outcome is "unknown" + + Scenario: Create span from OTel span + Given an agent + And OTel span is created with local context as parent + And OTel span ends + Then Elastic bridged object is a span + Then Elastic bridged span has local context as parent + # outcome should not be inferred from the lack/presence of errors + Then Elastic bridged span outcome is "unknown" + + # --- OTel span kind mapping for spans & transactions + + Scenario Outline: OTel span kind for spans & default span type & subtype + Given an agent + And an active transaction + And OTel span is created with kind "" + And OTel span ends + Then Elastic bridged object is a span + Then Elastic bridged span OTel kind is "" + Then Elastic bridged span type is "" + Then Elastic bridged span subtype is "" + Examples: + | kind | default_type | default_subtype | + | INTERNAL | app | internal | + | SERVER | unknown | | + | CLIENT | unknown | | + | PRODUCER | unknown | | + | CONSUMER | unknown | | + + Scenario Outline: OTel span kind for transactions & default transaction type + Given an agent + And OTel span is created with kind "" + And OTel span ends + Then Elastic bridged object is a transaction + Then Elastic bridged transaction OTel kind is "" + Then Elastic bridged transaction type is 'unknown' + Examples: + | kind | + | INTERNAL | + | SERVER | + | CLIENT | + | PRODUCER | + | CONSUMER | + + # OTel span status mapping for spans & transactions + + Scenario Outline: OTel span mapping with status for transactions + Given an agent + And OTel span is created with kind 'SERVER' + And OTel span status set to "" + And OTel span ends + Then Elastic bridged object is a transaction + Then Elastic bridged transaction outcome is "" + Examples: + | status | outcome | + | unset | unknown | + | ok | success | + | error | failure | + + Scenario Outline: OTel span mapping with status for spans + Given an agent + Given an active transaction + And OTel span is created with kind 'INTERNAL' + And OTel span status set to "" + And OTel span ends + Then Elastic bridged object is a span + Then Elastic bridged span outcome is "" + Examples: + | status | outcome | + | unset | unknown | + | ok | success | + | error | failure | + + # --- span type, subtype and action inference from OTel attributes + + # --- HTTP server + # https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#http-server + Scenario Outline: HTTP server [ ] + Given an agent + And OTel span is created with kind 'SERVER' + And OTel span has following attributes + | http.url | | + | http.scheme | | + And OTel span ends + Then Elastic bridged object is a transaction + Then Elastic bridged transaction type is "request" + Examples: + | http.url | http.scheme | + | http://testing.invalid/ | | + | | http | + + # --- HTTP client + # https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#http-client + Scenario Outline: HTTP client [ ] + Given an agent + And an active transaction + And OTel span is created with kind 'CLIENT' + And OTel span has following attributes + | http.url | | + | http.scheme | | + | http.host | | + | net.peer.ip | | + | net.peer.name | | + | net.peer.port | | + And OTel span ends + Then Elastic bridged span type is 'external' + Then Elastic bridged span subtype is 'http' + Then Elastic bridged span OTel attributes are copied as-is + Then Elastic bridged span destination resource is set to "" + Examples: + | http.url | http.scheme | http.host | net.peer.ip | net.peer.name | net.peer.port | resource | + | https://testing.invalid:8443/ | | | | | | testing.invalid:8443 | + | https://[::1]/ | | | | | | [::1]:443 | + | http://testing.invalid/ | | | | | | testing.invalid:80 | + | | http | testing.invalid | | | | testing.invalid:80 | + | | https | testing.invalid | 127.0.0.1 | | | testing.invalid:443 | + | | http | | 127.0.0.1 | | 81 | 127.0.0.1:81 | + | | https | | 127.0.0.1 | | 445 | 127.0.0.1:445 | + | | http | | 127.0.0.1 | host1 | 445 | host1:445 | + | | https | | 127.0.0.1 | host2 | 445 | host2:445 | + + # --- DB client + # https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/database.md + Scenario Outline: DB client [ ] + Given an agent + And an active transaction + And OTel span is created with kind 'CLIENT' + And OTel span has following attributes + | db.system | | + | db.name | | + | net.peer.ip | | + | net.peer.name | | + | net.peer.port | | + And OTel span ends + Then Elastic bridged span type is 'db' + Then Elastic bridged span subtype is "" + Then Elastic bridged span OTel attributes are copied as-is + Then Elastic bridged span destination resource is set to "" + Examples: + | db.system | db.name | net.peer.ip | net.peer.name | net.peer.port | resource | + | mysql | | | | | mysql | + | oracle | | | oracledb | | oracledb | + | oracle | | 127.0.0.1 | | | 127.0.0.1 | + | mysql | | 127.0.0.1 | dbserver | 3307 | dbserver:3307 | + | mysql | myDb | | | | mysql/myDb | + | oracle | myDb | | oracledb | | oracledb/myDb | + | oracle | myDb | 127.0.0.1 | | | 127.0.0.1/myDb | + | mysql | myDb | 127.0.0.1 | dbserver | 3307 | dbserver:3307/myDb | + + # --- Messaging consumer (transaction consuming/receiving a message) + # https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/messaging.md + Scenario: Messaging consumer + Given an agent + And OTel span is created with kind 'CONSUMER' + And OTel span has following attributes + | messaging.system | anything | + And OTel span ends + Then Elastic bridged transaction type is 'messaging' + + # --- Messaging producer (client span emitting a message) + # https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/messaging.md + Scenario Outline: Messaging producer [ ] + Given an agent + And an active transaction + And OTel span is created with kind 'PRODUCER' + And OTel span has following attributes + | messaging.system | | + | messaging.destination | | + | messaging.url | | + | net.peer.ip | | + | net.peer.name | | + | net.peer.port | | + And OTel span ends + Then Elastic bridged span type is 'messaging' + Then Elastic bridged span subtype is "" + Then Elastic bridged span OTel attributes are copied as-is + Then Elastic bridged span destination resource is set to "" + Examples: + | messaging.system | messaging.destination | messaging.url | net.peer.ip | net.peer.name | net.peer.port | resource | + | rabbitmq | | amqp://carrot:4444/q1 | | | | carrot:4444 | + | rabbitmq | | | 127.0.0.1 | carrot-server | 7777 | carrot-server:7777 | + | rabbitmq | | | | carrot-server | | carrot-server | + | rabbitmq | | | 127.0.0.1 | | | 127.0.0.1 | + | rabbitmq | myQueue | amqp://carrot:4444/q1 | | | | carrot:4444/myQueue | + | rabbitmq | myQueue | | 127.0.0.1 | carrot-server | 7777 | carrot-server:7777/myQueue | + | rabbitmq | myQueue | | | carrot-server | | carrot-server/myQueue | + | rabbitmq | myQueue | | 127.0.0.1 | | | 127.0.0.1/myQueue | + + # --- RPC client + # https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/rpc.md + Scenario Outline: RPC client [ ] + Given an agent + And an active transaction + And OTel span is created with kind 'CLIENT' + And OTel span has following attributes + | rpc.system | | + | rpc.service | | + | net.peer.ip | | + | net.peer.name | | + | net.peer.port | | + And OTel span ends + Then Elastic bridged span type is 'external' + Then Elastic bridged span subtype is "" + Then Elastic bridged span OTel attributes are copied as-is + Then Elastic bridged span destination resource is set to "" + Examples: + | rpc.system | rpc.service | net.peer.ip | net.peer.name | net.peer.port | resource | + | grpc | | | | | grpc | + | grpc | myService | | | | grpc/myService | + | grpc | myService | | rpc-server | | rpc-server/myService | + | grpc | myService | 127.0.0.1 | rpc-server | | rpc-server/myService | + | grpc | | 127.0.0.1 | rpc-server | 7777 | rpc-server:7777 | + | grpc | myService | 127.0.0.1 | rpc-server | 7777 | rpc-server:7777/myService | + | grpc | myService | 127.0.0.1 | | 7777 | 127.0.0.1:7777/myService | + + # --- RPC server + # https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/rpc.md + Scenario: RPC server + Given an agent + And OTel span is created with kind 'SERVER' + And OTel span has following attributes + | rpc.system | grpc | + And OTel span ends + Then Elastic bridged transaction type is 'request' + diff --git a/apm-agent-plugin-sdk/pom.xml b/apm-agent-plugin-sdk/pom.xml index 59e2ed1a55..61bd32cb55 100644 --- a/apm-agent-plugin-sdk/pom.xml +++ b/apm-agent-plugin-sdk/pom.xml @@ -3,7 +3,7 @@ apm-agent-parent co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/apm-agent-plugins/apm-apache-httpclient-plugin/pom.xml b/apm-agent-plugins/apm-apache-httpclient-plugin/pom.xml index 584c0cec7b..4ef61e1cfb 100644 --- a/apm-agent-plugins/apm-apache-httpclient-plugin/pom.xml +++ b/apm-agent-plugins/apm-apache-httpclient-plugin/pom.xml @@ -5,7 +5,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-apache-httpclient-plugin diff --git a/apm-agent-plugins/apm-api-plugin/pom.xml b/apm-agent-plugins/apm-api-plugin/pom.xml index 97dc8778e2..0da3ed3130 100644 --- a/apm-agent-plugins/apm-api-plugin/pom.xml +++ b/apm-agent-plugins/apm-api-plugin/pom.xml @@ -5,7 +5,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT diff --git a/apm-agent-plugins/apm-asynchttpclient-plugin/pom.xml b/apm-agent-plugins/apm-asynchttpclient-plugin/pom.xml index 20e38ac8b5..f40c00a911 100644 --- a/apm-agent-plugins/apm-asynchttpclient-plugin/pom.xml +++ b/apm-agent-plugins/apm-asynchttpclient-plugin/pom.xml @@ -5,7 +5,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-asynchttpclient-plugin diff --git a/apm-agent-plugins/apm-awslambda-plugin/pom.xml b/apm-agent-plugins/apm-awslambda-plugin/pom.xml index e0f984e866..4d1c0d34ac 100644 --- a/apm-agent-plugins/apm-awslambda-plugin/pom.xml +++ b/apm-agent-plugins/apm-awslambda-plugin/pom.xml @@ -3,7 +3,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/apm-agent-plugins/apm-cassandra/apm-cassandra-core-plugin/pom.xml b/apm-agent-plugins/apm-cassandra/apm-cassandra-core-plugin/pom.xml index a9eb008bcb..b000dad28e 100644 --- a/apm-agent-plugins/apm-cassandra/apm-cassandra-core-plugin/pom.xml +++ b/apm-agent-plugins/apm-cassandra/apm-cassandra-core-plugin/pom.xml @@ -3,7 +3,7 @@ apm-cassandra co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/apm-agent-plugins/apm-cassandra/apm-cassandra3-plugin/pom.xml b/apm-agent-plugins/apm-cassandra/apm-cassandra3-plugin/pom.xml index 09fe59c3d3..9f14af471b 100644 --- a/apm-agent-plugins/apm-cassandra/apm-cassandra3-plugin/pom.xml +++ b/apm-agent-plugins/apm-cassandra/apm-cassandra3-plugin/pom.xml @@ -3,7 +3,7 @@ apm-cassandra co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/apm-agent-plugins/apm-cassandra/apm-cassandra4-plugin/pom.xml b/apm-agent-plugins/apm-cassandra/apm-cassandra4-plugin/pom.xml index 86b791e3f1..08931bc099 100644 --- a/apm-agent-plugins/apm-cassandra/apm-cassandra4-plugin/pom.xml +++ b/apm-agent-plugins/apm-cassandra/apm-cassandra4-plugin/pom.xml @@ -3,7 +3,7 @@ apm-cassandra co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/apm-agent-plugins/apm-cassandra/pom.xml b/apm-agent-plugins/apm-cassandra/pom.xml index d64faebf6f..297dd610eb 100644 --- a/apm-agent-plugins/apm-cassandra/pom.xml +++ b/apm-agent-plugins/apm-cassandra/pom.xml @@ -3,7 +3,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/apm-agent-plugins/apm-dubbo-plugin/pom.xml b/apm-agent-plugins/apm-dubbo-plugin/pom.xml index fa4b3b7285..20f2b8c561 100644 --- a/apm-agent-plugins/apm-dubbo-plugin/pom.xml +++ b/apm-agent-plugins/apm-dubbo-plugin/pom.xml @@ -3,7 +3,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/apm-agent-plugins/apm-ecs-logging-plugin/pom.xml b/apm-agent-plugins/apm-ecs-logging-plugin/pom.xml index b753b44c03..27eb6c3cea 100644 --- a/apm-agent-plugins/apm-ecs-logging-plugin/pom.xml +++ b/apm-agent-plugins/apm-ecs-logging-plugin/pom.xml @@ -5,7 +5,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-ecs-logging-plugin diff --git a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/pom.xml b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/pom.xml index 0cebba5ab4..fc96731626 100644 --- a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/pom.xml +++ b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-5_6/pom.xml @@ -5,7 +5,7 @@ apm-es-restclient-plugin co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-es-restclient-plugin-5_6 diff --git a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/pom.xml b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/pom.xml index db7e3854b2..3cea906adf 100644 --- a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/pom.xml +++ b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-6_4/pom.xml @@ -5,7 +5,7 @@ apm-es-restclient-plugin co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-es-restclient-plugin-6_4 diff --git a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-7_x/pom.xml b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-7_x/pom.xml index b9cec05f19..36d54c5745 100644 --- a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-7_x/pom.xml +++ b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-7_x/pom.xml @@ -5,7 +5,7 @@ apm-es-restclient-plugin co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-es-restclient-plugin-7_x diff --git a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-common/pom.xml b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-common/pom.xml index 703dd61089..4e09236923 100644 --- a/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-common/pom.xml +++ b/apm-agent-plugins/apm-es-restclient-plugin/apm-es-restclient-plugin-common/pom.xml @@ -5,7 +5,7 @@ apm-es-restclient-plugin co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-es-restclient-plugin-common diff --git a/apm-agent-plugins/apm-es-restclient-plugin/pom.xml b/apm-agent-plugins/apm-es-restclient-plugin/pom.xml index bd54ada6fa..2e13607c1b 100644 --- a/apm-agent-plugins/apm-es-restclient-plugin/pom.xml +++ b/apm-agent-plugins/apm-es-restclient-plugin/pom.xml @@ -5,7 +5,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-es-restclient-plugin diff --git a/apm-agent-plugins/apm-grails-plugin/pom.xml b/apm-agent-plugins/apm-grails-plugin/pom.xml index 9f7941c797..797c9148d2 100644 --- a/apm-agent-plugins/apm-grails-plugin/pom.xml +++ b/apm-agent-plugins/apm-grails-plugin/pom.xml @@ -5,7 +5,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-grails-plugin diff --git a/apm-agent-plugins/apm-grpc/apm-grpc-plugin/pom.xml b/apm-agent-plugins/apm-grpc/apm-grpc-plugin/pom.xml index a274dab997..c77876dbed 100644 --- a/apm-agent-plugins/apm-grpc/apm-grpc-plugin/pom.xml +++ b/apm-agent-plugins/apm-grpc/apm-grpc-plugin/pom.xml @@ -5,7 +5,7 @@ co.elastic.apm apm-grpc - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-grpc-plugin diff --git a/apm-agent-plugins/apm-grpc/apm-grpc-test-1.6.1/pom.xml b/apm-agent-plugins/apm-grpc/apm-grpc-test-1.6.1/pom.xml index deb6826a5e..c138cfa990 100644 --- a/apm-agent-plugins/apm-grpc/apm-grpc-test-1.6.1/pom.xml +++ b/apm-agent-plugins/apm-grpc/apm-grpc-test-1.6.1/pom.xml @@ -5,7 +5,7 @@ co.elastic.apm apm-grpc - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-grpc-test-1.6.1 diff --git a/apm-agent-plugins/apm-grpc/apm-grpc-test-latest/pom.xml b/apm-agent-plugins/apm-grpc/apm-grpc-test-latest/pom.xml index 7ae82986a7..077093803f 100644 --- a/apm-agent-plugins/apm-grpc/apm-grpc-test-latest/pom.xml +++ b/apm-agent-plugins/apm-grpc/apm-grpc-test-latest/pom.xml @@ -5,7 +5,7 @@ co.elastic.apm apm-grpc - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-grpc-test-latest diff --git a/apm-agent-plugins/apm-grpc/pom.xml b/apm-agent-plugins/apm-grpc/pom.xml index 0939e0ca88..3b8255263b 100644 --- a/apm-agent-plugins/apm-grpc/pom.xml +++ b/apm-agent-plugins/apm-grpc/pom.xml @@ -5,7 +5,7 @@ co.elastic.apm apm-agent-plugins - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-grpc diff --git a/apm-agent-plugins/apm-hibernate-search-plugin/apm-hibernate-search-plugin-5_x/pom.xml b/apm-agent-plugins/apm-hibernate-search-plugin/apm-hibernate-search-plugin-5_x/pom.xml index 9b3d82c62a..c86a2ad3d1 100644 --- a/apm-agent-plugins/apm-hibernate-search-plugin/apm-hibernate-search-plugin-5_x/pom.xml +++ b/apm-agent-plugins/apm-hibernate-search-plugin/apm-hibernate-search-plugin-5_x/pom.xml @@ -5,7 +5,7 @@ apm-hibernate-search-plugin co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-hibernate-search-plugin-5_x diff --git a/apm-agent-plugins/apm-hibernate-search-plugin/apm-hibernate-search-plugin-6_x/pom.xml b/apm-agent-plugins/apm-hibernate-search-plugin/apm-hibernate-search-plugin-6_x/pom.xml index 115329f8d2..4dec17fb24 100644 --- a/apm-agent-plugins/apm-hibernate-search-plugin/apm-hibernate-search-plugin-6_x/pom.xml +++ b/apm-agent-plugins/apm-hibernate-search-plugin/apm-hibernate-search-plugin-6_x/pom.xml @@ -5,7 +5,7 @@ apm-hibernate-search-plugin co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-hibernate-search-plugin-6_x diff --git a/apm-agent-plugins/apm-hibernate-search-plugin/apm-hibernate-search-plugin-common/pom.xml b/apm-agent-plugins/apm-hibernate-search-plugin/apm-hibernate-search-plugin-common/pom.xml index 96cf1f7aa8..734fbac343 100644 --- a/apm-agent-plugins/apm-hibernate-search-plugin/apm-hibernate-search-plugin-common/pom.xml +++ b/apm-agent-plugins/apm-hibernate-search-plugin/apm-hibernate-search-plugin-common/pom.xml @@ -5,7 +5,7 @@ apm-hibernate-search-plugin co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-hibernate-search-plugin-common diff --git a/apm-agent-plugins/apm-hibernate-search-plugin/pom.xml b/apm-agent-plugins/apm-hibernate-search-plugin/pom.xml index 446d8fe5b2..9ca877963c 100644 --- a/apm-agent-plugins/apm-hibernate-search-plugin/pom.xml +++ b/apm-agent-plugins/apm-hibernate-search-plugin/pom.xml @@ -5,7 +5,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-hibernate-search-plugin diff --git a/apm-agent-plugins/apm-httpclient-core/pom.xml b/apm-agent-plugins/apm-httpclient-core/pom.xml index a010da12ac..065a6bd8db 100644 --- a/apm-agent-plugins/apm-httpclient-core/pom.xml +++ b/apm-agent-plugins/apm-httpclient-core/pom.xml @@ -5,7 +5,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-httpclient-core diff --git a/apm-agent-plugins/apm-httpserver-core/pom.xml b/apm-agent-plugins/apm-httpserver-core/pom.xml index 2deb1d3e45..25ea503b65 100644 --- a/apm-agent-plugins/apm-httpserver-core/pom.xml +++ b/apm-agent-plugins/apm-httpserver-core/pom.xml @@ -5,7 +5,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-httpserver-core diff --git a/apm-agent-plugins/apm-jakarta-websocket-plugin/pom.xml b/apm-agent-plugins/apm-jakarta-websocket-plugin/pom.xml index 302a6d3045..478a480ddd 100644 --- a/apm-agent-plugins/apm-jakarta-websocket-plugin/pom.xml +++ b/apm-agent-plugins/apm-jakarta-websocket-plugin/pom.xml @@ -5,7 +5,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-jakarta-websocket-plugin diff --git a/apm-agent-plugins/apm-java-concurrent-plugin/pom.xml b/apm-agent-plugins/apm-java-concurrent-plugin/pom.xml index 85d1bba7b5..79d36b2a2c 100644 --- a/apm-agent-plugins/apm-java-concurrent-plugin/pom.xml +++ b/apm-agent-plugins/apm-java-concurrent-plugin/pom.xml @@ -5,7 +5,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-java-concurrent-plugin diff --git a/apm-agent-plugins/apm-javalin-plugin/pom.xml b/apm-agent-plugins/apm-javalin-plugin/pom.xml index 78b3238c4f..d15c2fb192 100644 --- a/apm-agent-plugins/apm-javalin-plugin/pom.xml +++ b/apm-agent-plugins/apm-javalin-plugin/pom.xml @@ -5,7 +5,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-javalin-plugin diff --git a/apm-agent-plugins/apm-jaxrs-plugin-jakartaee-test/pom.xml b/apm-agent-plugins/apm-jaxrs-plugin-jakartaee-test/pom.xml index 4ceba9c1c0..d6c6a10ae8 100644 --- a/apm-agent-plugins/apm-jaxrs-plugin-jakartaee-test/pom.xml +++ b/apm-agent-plugins/apm-jaxrs-plugin-jakartaee-test/pom.xml @@ -3,7 +3,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/apm-agent-plugins/apm-jaxrs-plugin/pom.xml b/apm-agent-plugins/apm-jaxrs-plugin/pom.xml index f7f039eae0..a31e4cafd1 100644 --- a/apm-agent-plugins/apm-jaxrs-plugin/pom.xml +++ b/apm-agent-plugins/apm-jaxrs-plugin/pom.xml @@ -5,7 +5,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-jaxrs-plugin diff --git a/apm-agent-plugins/apm-jaxws-plugin-jakartaee-test/pom.xml b/apm-agent-plugins/apm-jaxws-plugin-jakartaee-test/pom.xml index 675555795d..193846170e 100644 --- a/apm-agent-plugins/apm-jaxws-plugin-jakartaee-test/pom.xml +++ b/apm-agent-plugins/apm-jaxws-plugin-jakartaee-test/pom.xml @@ -3,7 +3,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/apm-agent-plugins/apm-jaxws-plugin/pom.xml b/apm-agent-plugins/apm-jaxws-plugin/pom.xml index 28308c9b57..c292444e47 100644 --- a/apm-agent-plugins/apm-jaxws-plugin/pom.xml +++ b/apm-agent-plugins/apm-jaxws-plugin/pom.xml @@ -5,7 +5,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-jaxws-plugin diff --git a/apm-agent-plugins/apm-jdbc-plugin/pom.xml b/apm-agent-plugins/apm-jdbc-plugin/pom.xml index 110fa846aa..715f7835e8 100644 --- a/apm-agent-plugins/apm-jdbc-plugin/pom.xml +++ b/apm-agent-plugins/apm-jdbc-plugin/pom.xml @@ -5,7 +5,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-jdbc-plugin diff --git a/apm-agent-plugins/apm-jdk-httpclient-plugin/pom.xml b/apm-agent-plugins/apm-jdk-httpclient-plugin/pom.xml index cbad9b9e86..922d06f6f3 100644 --- a/apm-agent-plugins/apm-jdk-httpclient-plugin/pom.xml +++ b/apm-agent-plugins/apm-jdk-httpclient-plugin/pom.xml @@ -5,7 +5,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-jdk-httpclient-plugin diff --git a/apm-agent-plugins/apm-jdk-httpserver-plugin/pom.xml b/apm-agent-plugins/apm-jdk-httpserver-plugin/pom.xml index c5b2635ce9..2443628532 100644 --- a/apm-agent-plugins/apm-jdk-httpserver-plugin/pom.xml +++ b/apm-agent-plugins/apm-jdk-httpserver-plugin/pom.xml @@ -5,7 +5,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-jdk-httpserver-plugin diff --git a/apm-agent-plugins/apm-jms-plugin/apm-jms-plugin-base/pom.xml b/apm-agent-plugins/apm-jms-plugin/apm-jms-plugin-base/pom.xml index 1fc3885ffc..51837eb21f 100644 --- a/apm-agent-plugins/apm-jms-plugin/apm-jms-plugin-base/pom.xml +++ b/apm-agent-plugins/apm-jms-plugin/apm-jms-plugin-base/pom.xml @@ -5,7 +5,7 @@ apm-jms-plugin co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-jms-plugin-base diff --git a/apm-agent-plugins/apm-jms-plugin/apm-jms-spring-plugin/pom.xml b/apm-agent-plugins/apm-jms-plugin/apm-jms-spring-plugin/pom.xml index 5d00865d59..eca76acbd0 100644 --- a/apm-agent-plugins/apm-jms-plugin/apm-jms-spring-plugin/pom.xml +++ b/apm-agent-plugins/apm-jms-plugin/apm-jms-spring-plugin/pom.xml @@ -5,7 +5,7 @@ apm-jms-plugin co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-jms-spring-plugin diff --git a/apm-agent-plugins/apm-jms-plugin/pom.xml b/apm-agent-plugins/apm-jms-plugin/pom.xml index 3f9c60b0f7..3ab71594c7 100644 --- a/apm-agent-plugins/apm-jms-plugin/pom.xml +++ b/apm-agent-plugins/apm-jms-plugin/pom.xml @@ -5,7 +5,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-jms-plugin diff --git a/apm-agent-plugins/apm-jmx-plugin/pom.xml b/apm-agent-plugins/apm-jmx-plugin/pom.xml index d1810cc9f8..0bba656b3d 100644 --- a/apm-agent-plugins/apm-jmx-plugin/pom.xml +++ b/apm-agent-plugins/apm-jmx-plugin/pom.xml @@ -5,7 +5,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-jmx-plugin diff --git a/apm-agent-plugins/apm-jsf-plugin/pom.xml b/apm-agent-plugins/apm-jsf-plugin/pom.xml index c3ac10d548..bed5aabd6b 100644 --- a/apm-agent-plugins/apm-jsf-plugin/pom.xml +++ b/apm-agent-plugins/apm-jsf-plugin/pom.xml @@ -5,7 +5,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-jsf-plugin diff --git a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-base-plugin/pom.xml b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-base-plugin/pom.xml index 836774b645..019f80e0c3 100644 --- a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-base-plugin/pom.xml +++ b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-base-plugin/pom.xml @@ -3,7 +3,7 @@ apm-kafka-plugin co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/pom.xml b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/pom.xml index eb63b9f22d..5f6c869b72 100644 --- a/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/pom.xml +++ b/apm-agent-plugins/apm-kafka-plugin/apm-kafka-headers-plugin/pom.xml @@ -4,7 +4,7 @@ apm-kafka-plugin co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/apm-agent-plugins/apm-kafka-plugin/pom.xml b/apm-agent-plugins/apm-kafka-plugin/pom.xml index 155f636066..98e8e2900f 100644 --- a/apm-agent-plugins/apm-kafka-plugin/pom.xml +++ b/apm-agent-plugins/apm-kafka-plugin/pom.xml @@ -3,7 +3,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/apm-agent-plugins/apm-log-correlation-plugin/pom.xml b/apm-agent-plugins/apm-log-correlation-plugin/pom.xml new file mode 100644 index 0000000000..2ee34e9689 --- /dev/null +++ b/apm-agent-plugins/apm-log-correlation-plugin/pom.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + + + apm-agent-plugins + co.elastic.apm + 1.30.0-SNAPSHOT + + + apm-log-correlation-plugin + ${project.groupId}:${project.artifactId} + + + ${project.basedir}/../.. + + + + + + ch.qos.logback + logback-classic + 1.2.3 + test + + + org.apache.logging.log4j + log4j-core + 2.12.0 + test + + + log4j + log4j + 1.2.17 + test + + + org.jboss.logging + jboss-logging + 3.4.1.Final + test + + + ${project.groupId} + apm-error-logging-plugin + ${project.version} + test + + + + diff --git a/apm-agent-plugins/apm-log-shipper-plugin/pom.xml b/apm-agent-plugins/apm-log-shipper-plugin/pom.xml index b13385a153..1c993fb706 100644 --- a/apm-agent-plugins/apm-log-shipper-plugin/pom.xml +++ b/apm-agent-plugins/apm-log-shipper-plugin/pom.xml @@ -5,7 +5,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-log-shipper-plugin diff --git a/apm-agent-plugins/apm-logging-plugin/apm-jboss-logging-plugin/pom.xml b/apm-agent-plugins/apm-logging-plugin/apm-jboss-logging-plugin/pom.xml index 6989066900..db981c737d 100644 --- a/apm-agent-plugins/apm-logging-plugin/apm-jboss-logging-plugin/pom.xml +++ b/apm-agent-plugins/apm-logging-plugin/apm-jboss-logging-plugin/pom.xml @@ -5,7 +5,7 @@ apm-logging-plugin co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-jboss-logging-plugin diff --git a/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/pom.xml b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/pom.xml index f907224e71..8ff500ab69 100644 --- a/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/pom.xml +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j1-plugin/pom.xml @@ -5,7 +5,7 @@ apm-logging-plugin co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-log4j1-plugin diff --git a/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/pom.xml b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/pom.xml index 334a39ad13..d4b3f7db29 100644 --- a/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/pom.xml +++ b/apm-agent-plugins/apm-logging-plugin/apm-log4j2-plugin/pom.xml @@ -5,7 +5,7 @@ apm-logging-plugin co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-log4j2-plugin diff --git a/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/pom.xml b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/pom.xml index 50c956e68c..0ca785bee7 100644 --- a/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/pom.xml +++ b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-impl/pom.xml @@ -5,7 +5,7 @@ apm-logback-plugin co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-logback-plugin-impl diff --git a/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-legacy-tests/pom.xml b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-legacy-tests/pom.xml index 6a0b8995b1..dda18b6800 100644 --- a/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-legacy-tests/pom.xml +++ b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/apm-logback-plugin-legacy-tests/pom.xml @@ -5,7 +5,7 @@ apm-logback-plugin co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-logback-plugin-legacy-tests diff --git a/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/pom.xml b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/pom.xml index f5a20136e3..1396ae5b95 100644 --- a/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/pom.xml +++ b/apm-agent-plugins/apm-logging-plugin/apm-logback-plugin/pom.xml @@ -5,7 +5,7 @@ apm-logging-plugin co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-logback-plugin diff --git a/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/pom.xml b/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/pom.xml index 254ea7d105..8a9c0c6bf0 100644 --- a/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/pom.xml +++ b/apm-agent-plugins/apm-logging-plugin/apm-logging-plugin-common/pom.xml @@ -5,7 +5,7 @@ apm-logging-plugin co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-logging-plugin-common diff --git a/apm-agent-plugins/apm-logging-plugin/apm-slf4j-plugin/pom.xml b/apm-agent-plugins/apm-logging-plugin/apm-slf4j-plugin/pom.xml index bcb2f3c400..2ad5f2e96c 100644 --- a/apm-agent-plugins/apm-logging-plugin/apm-slf4j-plugin/pom.xml +++ b/apm-agent-plugins/apm-logging-plugin/apm-slf4j-plugin/pom.xml @@ -5,7 +5,7 @@ apm-logging-plugin co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-slf4j-plugin diff --git a/apm-agent-plugins/apm-logging-plugin/pom.xml b/apm-agent-plugins/apm-logging-plugin/pom.xml index f720f4d04f..33c7e26025 100644 --- a/apm-agent-plugins/apm-logging-plugin/pom.xml +++ b/apm-agent-plugins/apm-logging-plugin/pom.xml @@ -5,7 +5,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-logging-plugin diff --git a/apm-agent-plugins/apm-micrometer-plugin/pom.xml b/apm-agent-plugins/apm-micrometer-plugin/pom.xml index fb240ead02..6b203378ac 100644 --- a/apm-agent-plugins/apm-micrometer-plugin/pom.xml +++ b/apm-agent-plugins/apm-micrometer-plugin/pom.xml @@ -3,7 +3,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/apm-agent-plugins/apm-mongoclient-plugin/pom.xml b/apm-agent-plugins/apm-mongoclient-plugin/pom.xml index 0997a5d3ef..ebbc7a50ee 100644 --- a/apm-agent-plugins/apm-mongoclient-plugin/pom.xml +++ b/apm-agent-plugins/apm-mongoclient-plugin/pom.xml @@ -3,7 +3,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/apm-agent-plugins/apm-okhttp-plugin/pom.xml b/apm-agent-plugins/apm-okhttp-plugin/pom.xml index 18ec29b0c7..82d937e406 100644 --- a/apm-agent-plugins/apm-okhttp-plugin/pom.xml +++ b/apm-agent-plugins/apm-okhttp-plugin/pom.xml @@ -5,7 +5,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-okhttp-plugin diff --git a/apm-agent-plugins/apm-okhttp-test/pom.xml b/apm-agent-plugins/apm-okhttp-test/pom.xml index f36e10c091..f6d3ff5eab 100644 --- a/apm-agent-plugins/apm-okhttp-test/pom.xml +++ b/apm-agent-plugins/apm-okhttp-test/pom.xml @@ -3,7 +3,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/pom.xml b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/pom.xml new file mode 100644 index 0000000000..5a0f0f355a --- /dev/null +++ b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/pom.xml @@ -0,0 +1,81 @@ + + + 4.0.0 + + co.elastic.apm + apm-opentelemetry + 1.30.0-SNAPSHOT + + + apm-opentelemetry-plugin + ${project.groupId}:${project.artifactId} + + + ${project.basedir}/../../.. + + + 1.0.0 + 1.0.0-alpha + + + + + io.opentelemetry + opentelemetry-api + + ${version.opentelemetry} + provided + + + io.opentelemetry + opentelemetry-semconv + ${version.opentelemetry.semconv} + provided + + + ${project.groupId} + apm-httpclient-core + ${project.version} + + + + + io.cucumber + cucumber-java + ${version.cucumber} + test + + + io.cucumber + cucumber-junit + ${version.cucumber} + test + + + io.cucumber + cucumber-picocontainer + ${version.cucumber} + test + + + + + + + maven-jar-plugin + + + + test-jar + + + + + + + diff --git a/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/AbstractOpenTelemetryInstrumentation.java b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/AbstractOpenTelemetryInstrumentation.java new file mode 100644 index 0000000000..0492594875 --- /dev/null +++ b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/AbstractOpenTelemetryInstrumentation.java @@ -0,0 +1,45 @@ +/* + * 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.opentelemetry; + +import co.elastic.apm.agent.bci.TracerAwareInstrumentation; +import co.elastic.apm.agent.bci.bytebuddy.CustomElementMatchers; +import net.bytebuddy.matcher.ElementMatcher; + +import java.util.Arrays; +import java.util.Collection; + +public abstract class AbstractOpenTelemetryInstrumentation extends TracerAwareInstrumentation { + + @Override + public final ElementMatcher.Junction getClassLoaderMatcher() { + return CustomElementMatchers.classLoaderCanLoadClass("io.opentelemetry.context.propagation.TextMapSetter"); + } + + @Override + public final boolean includeWhenInstrumentationIsDisabled() { + return true; + } + + + @Override + public final Collection getInstrumentationGroupNames() { + return Arrays.asList("opentelemetry", "experimental"); + } +} diff --git a/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/ArrayBasedContextInstrumentation.java b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/ArrayBasedContextInstrumentation.java new file mode 100644 index 0000000000..798e48574d --- /dev/null +++ b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/ArrayBasedContextInstrumentation.java @@ -0,0 +1,75 @@ +/* + * 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.opentelemetry; + +import co.elastic.apm.agent.impl.GlobalTracer; +import co.elastic.apm.agent.opentelemetry.sdk.OTelBridgeContext; +import io.opentelemetry.context.Context; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +import javax.annotation.Nullable; + +import static net.bytebuddy.matcher.ElementMatchers.hasSuperType; +import static net.bytebuddy.matcher.ElementMatchers.isStatic; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.returns; +import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments; + +/** + * Instruments {@code io.opentelemetry.context.ArrayBasedContext#root()} to capture original context root + * and allows relying on the provided context implementation for key/value storage in context + */ +public class ArrayBasedContextInstrumentation extends AbstractOpenTelemetryInstrumentation { + + @Override + public ElementMatcher getTypeMatcher() { + return named("io.opentelemetry.context.ArrayBasedContext"); + } + + @Override + public ElementMatcher getMethodMatcher() { + return named("root") + .and(isStatic()) + .and(returns(hasSuperType(named("io.opentelemetry.context.Context")))) + .and(takesNoArguments()); + } + + @Override + public String getAdviceClassName() { + return "co.elastic.apm.agent.opentelemetry.ArrayBasedContextInstrumentation$RootAdvice"; + } + + public static class RootAdvice { + + @Nullable + @Advice.AssignReturned.ToReturned + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class, inline = false) + public static Context onExit(@Advice.Return @Nullable Context returnValue) { + + if (returnValue == null) { + return null; + } + + return OTelBridgeContext.bridgeRootContext(GlobalTracer.requireTracerImpl(), returnValue); + } + } +} diff --git a/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/ContextStorageInstrumentation.java b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/ContextStorageInstrumentation.java new file mode 100644 index 0000000000..5ff64f53a5 --- /dev/null +++ b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/ContextStorageInstrumentation.java @@ -0,0 +1,62 @@ +/* + * 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.opentelemetry; + +import co.elastic.apm.agent.impl.GlobalTracer; +import co.elastic.apm.agent.opentelemetry.context.OTelContextStorage; +import io.opentelemetry.context.ContextStorage; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.returns; + +/** + * Instruments {@link ContextStorage#get()} + */ +public class ContextStorageInstrumentation extends AbstractOpenTelemetryInstrumentation { + + @Override + public ElementMatcher getTypeMatcher() { + return named("io.opentelemetry.context.ContextStorage"); + } + + @Override + public ElementMatcher getMethodMatcher() { + return named("get").and(returns(named("io.opentelemetry.context.ContextStorage"))); + } + + @Override + public String getAdviceClassName() { + return "co.elastic.apm.agent.opentelemetry.ContextStorageInstrumentation$ContextStorageAdvice"; + } + + public static class ContextStorageAdvice { + + private static final OTelContextStorage CONTEXT_STORAGE = new OTelContextStorage(GlobalTracer.requireTracerImpl()); + + @Advice.AssignReturned.ToReturned + @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + public static ContextStorage onExit() { + return CONTEXT_STORAGE; + } + } +} diff --git a/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/GlobalOpenTelemetryInstrumentation.java b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/GlobalOpenTelemetryInstrumentation.java new file mode 100644 index 0000000000..94a620f519 --- /dev/null +++ b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/GlobalOpenTelemetryInstrumentation.java @@ -0,0 +1,68 @@ +/* + * 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.opentelemetry; + +import co.elastic.apm.agent.impl.GlobalTracer; +import co.elastic.apm.agent.opentelemetry.sdk.ElasticOpenTelemetry; +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +import static net.bytebuddy.matcher.ElementMatchers.named; + +/** + * Instruments {@link GlobalOpenTelemetry#get()} + */ +public class GlobalOpenTelemetryInstrumentation extends AbstractOpenTelemetryInstrumentation { + + @Override + public ElementMatcher getTypeMatcher() { + return named("io.opentelemetry.api.GlobalOpenTelemetry"); + } + + @Override + public ElementMatcher getMethodMatcher() { + return named("get"); + } + + @Override + public String getAdviceClassName() { + return "co.elastic.apm.agent.opentelemetry.GlobalOpenTelemetryInstrumentation$GlobalOpenTelemetryAdvice"; + } + + public static class GlobalOpenTelemetryAdvice { + + private static final OpenTelemetry ELASTIC_OPEN_TELEMETRY = new ElasticOpenTelemetry(GlobalTracer.requireTracerImpl()); + + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false, skipOn = Advice.OnNonDefaultValue.class) + public static boolean onEnter() { + // skips actual method and directly goes to exit advice + return true; + } + + @Advice.AssignReturned.ToReturned + @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + public static OpenTelemetry onExit() { + return ELASTIC_OPEN_TELEMETRY; + } + } +} diff --git a/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/context/OTelContextStorage.java b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/context/OTelContextStorage.java new file mode 100644 index 0000000000..4d35b2e558 --- /dev/null +++ b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/context/OTelContextStorage.java @@ -0,0 +1,103 @@ +/* + * 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.opentelemetry.context; + +import co.elastic.apm.agent.impl.ElasticApmTracer; +import co.elastic.apm.agent.impl.transaction.AbstractSpan; +import co.elastic.apm.agent.impl.transaction.ElasticContext; +import co.elastic.apm.agent.opentelemetry.sdk.OTelBridgeContext; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.ContextStorage; +import io.opentelemetry.context.Scope; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nullable; + +public class OTelContextStorage implements ContextStorage { + + private static final Logger logger = LoggerFactory.getLogger(OTelContextStorage.class); + + private final ElasticApmTracer tracer; + + public OTelContextStorage(ElasticApmTracer tracer) { + this.tracer = tracer; + } + + @Override + public Scope attach(@Nullable Context toAttach) { + if (toAttach == null) { + // no context to attach + return Scope.noop(); + } + + if (toAttach == tracer.currentContext()) { + // already active + return Scope.noop(); + } + + if (!(toAttach instanceof OTelBridgeContext)) { + // likely to be triggered when trying to activate a context created before agent attachment and + // instrumentation. + logger.debug("unexpected context type for attachment {}", toAttach.getClass().getName()); + return Scope.noop(); + } + OTelBridgeContext bridgeContext = (OTelBridgeContext) toAttach; + + tracer.activate(bridgeContext); + return bridgeContext; + } + + @Nullable + @Override + public Context current() { + ElasticContext current = tracer.currentContext(); + if (current == null) { + return null; + } + + if (current instanceof OTelBridgeContext) { + // current context is already OTel compliant, no need to upgrade it + return (Context) current; + } + + if (!(current instanceof AbstractSpan)) { + throw new IllegalStateException("unexpected context type to upgrade: " + current.getClass().getName()); + } + + // At this stage, the currently active span is a "regular elastic span", we need to upgrade and replace it with + // a bridged implementation that allows to make "regular elastic span" visible in the OTel context. + + logger.debug("upgrading active context {} to a bridged context", current); + + tracer.deactivate(current); + + // Ensure that root context is being accessed at least once to capture the original root + // OTel 1.0 directly calls ArrayBasedContext.root() which is not publicly accessible, later versions delegate + // to ContextStorage.root() which we can't call from here either. + Context.root(); + + OTelBridgeContext upgradedContext = OTelBridgeContext.wrapElasticActiveSpan(tracer, (AbstractSpan) current); + tracer.activate(upgradedContext); + + logger.debug("active context upgraded to {}", upgradedContext); + + return upgradedContext; + } +} diff --git a/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/context/package-info.java b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/context/package-info.java new file mode 100644 index 0000000000..e4ee3b911b --- /dev/null +++ b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/context/package-info.java @@ -0,0 +1,22 @@ +/* + * 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. + */ +@NonnullApi +package co.elastic.apm.agent.opentelemetry.context; + +import co.elastic.apm.agent.sdk.NonnullApi; diff --git a/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/package-info.java b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/package-info.java new file mode 100644 index 0000000000..245461a82d --- /dev/null +++ b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/package-info.java @@ -0,0 +1,22 @@ +/* + * 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. + */ +@NonnullApi +package co.elastic.apm.agent.opentelemetry; + +import co.elastic.apm.agent.sdk.NonnullApi; diff --git a/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/sdk/ElasticOpenTelemetry.java b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/sdk/ElasticOpenTelemetry.java new file mode 100644 index 0000000000..afc86a3867 --- /dev/null +++ b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/sdk/ElasticOpenTelemetry.java @@ -0,0 +1,46 @@ +/* + * 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.opentelemetry.sdk; + +import co.elastic.apm.agent.impl.ElasticApmTracer; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.trace.TracerProvider; +import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; +import io.opentelemetry.context.propagation.ContextPropagators; + +public class ElasticOpenTelemetry implements OpenTelemetry { + + private final ContextPropagators contextPropagators; + private final TracerProvider tracerProvider; + + public ElasticOpenTelemetry(ElasticApmTracer tracer) { + tracerProvider = new OTelTracerProvider(new OTelTracer(tracer)); + contextPropagators = ContextPropagators.create(W3CTraceContextPropagator.getInstance()); + } + + @Override + public TracerProvider getTracerProvider() { + return tracerProvider; + } + + @Override + public ContextPropagators getPropagators() { + return contextPropagators; + } +} diff --git a/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/sdk/OTelBridgeContext.java b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/sdk/OTelBridgeContext.java new file mode 100644 index 0000000000..6eeec76608 --- /dev/null +++ b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/sdk/OTelBridgeContext.java @@ -0,0 +1,161 @@ +/* + * 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.opentelemetry.sdk; + +import co.elastic.apm.agent.impl.ElasticApmTracer; +import co.elastic.apm.agent.impl.transaction.AbstractSpan; +import co.elastic.apm.agent.impl.transaction.ElasticContext; +import co.elastic.apm.agent.impl.transaction.Transaction; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.ContextKey; +import io.opentelemetry.context.Scope; + +import javax.annotation.Nullable; +import java.util.Objects; + +/** + * Bridge implementation of OpenTelemetry {@link Context} that allows to provide compatibility with {@link ElasticContext}. + */ +public class OTelBridgeContext implements ElasticContext, Context, Scope { + + /** + * Original root context as returned by {@link Context#root()} before instrumentation. + */ + @Nullable + private static volatile Context originalRootContext; + + /** + * Bridged root context that will be returned by {@link Context#root()} after instrumentation + */ + @Nullable + private static volatile OTelBridgeContext root; + + /** + * OTel context used for key/value storage + */ + private final Context otelContext; + + private final ElasticApmTracer tracer; + + private OTelBridgeContext(ElasticApmTracer tracer, Context otelContext) { + this.tracer = tracer; + this.otelContext = otelContext; + } + + /** + * Captures the original root context and sets-up the bridged root if required + * + * @param tracer tracer + * @param originalRoot original OTel root context + * @return bridged context + */ + public static OTelBridgeContext bridgeRootContext(ElasticApmTracer tracer, Context originalRoot) { + if (root != null) { + return root; + } + + synchronized (OTelBridgeContext.class) { + if (root == null) { + originalRootContext = originalRoot; + root = new OTelBridgeContext(tracer, originalRoot); + } + } + return root; + } + + /** + * Bridges an active elastic span to an active OTel span context + * + * @param tracer tracer + * @param span elastic span + * @return bridged context + */ + public static OTelBridgeContext wrapElasticActiveSpan(ElasticApmTracer tracer, AbstractSpan span) { + OTelSpan otelSpan = new OTelSpan(span); + Objects.requireNonNull(originalRootContext, "OTel original context must be set through bridgeRootContext first"); + return new OTelBridgeContext(tracer, originalRootContext.with(otelSpan)); + } + + @Override + public OTelBridgeContext activate() { + tracer.activate(this); + return this; + } + + @Override + public co.elastic.apm.agent.impl.Scope activateInScope() { + return tracer.activateInScope(this); + } + + @Override + public OTelBridgeContext withActiveSpan(AbstractSpan span) { + // otel context is immutable, thus we have to create new bridged context here + return new OTelBridgeContext(tracer, otelContext.with(new OTelSpan(span))); + } + + @Override + public OTelBridgeContext deactivate() { + tracer.deactivate(this); + return this; + } + + @Nullable + @Override + public AbstractSpan getSpan() { + // get otel span from context + Span span = Span.fromContext(otelContext); + if (span instanceof OTelSpan) { + return ((OTelSpan) span).getInternalSpan(); + } + return null; + } + + @Nullable + @Override + public Transaction getTransaction() { + AbstractSpan span = getSpan(); + return span != null ? span.getTransaction() : null; + } + + // OTel context implementation + + @Nullable + @Override + public V get(ContextKey key) { + return otelContext.get(key); + } + + @Override + public Context with(ContextKey key, V value) { + return new OTelBridgeContext(tracer, otelContext.with(key, value)); + } + + // OTel scope implementation + + @Override + public void close() { + deactivate(); + } + + @Override + public String toString() { + return "OTelBridgeContext[" + otelContext + "]"; + } +} diff --git a/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/sdk/OTelSpan.java b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/sdk/OTelSpan.java new file mode 100644 index 0000000000..8113a5ade2 --- /dev/null +++ b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/sdk/OTelSpan.java @@ -0,0 +1,279 @@ +/* + * 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.opentelemetry.sdk; + +import co.elastic.apm.agent.impl.context.Url; +import co.elastic.apm.agent.impl.transaction.AbstractSpan; +import co.elastic.apm.agent.impl.transaction.OTelSpanKind; +import co.elastic.apm.agent.impl.transaction.Outcome; +import co.elastic.apm.agent.impl.transaction.Transaction; +import co.elastic.apm.agent.sdk.logging.Logger; +import co.elastic.apm.agent.sdk.logging.LoggerFactory; +import co.elastic.apm.agent.util.LoggerUtils; +import co.elastic.apm.agent.util.VersionUtils; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.api.trace.StatusCode; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +public class OTelSpan implements Span { + private static final Logger eventLogger = LoggerUtils.logOnce(LoggerFactory.getLogger(OTelSpan.class)); + + private final AbstractSpan span; + + public OTelSpan(AbstractSpan span) { + this.span = span; + span.incrementReferences(); + } + + @Override + public Span setAttribute(AttributeKey key, @Nonnull T value) { + span.getOtelAttributes().put(key.getKey(), value); + return this; + } + + @Override + public Span addEvent(String name, Attributes attributes) { + eventLogger.warn("The addEvent API is not supported at the moment"); + return this; + } + + @Override + public Span addEvent(String name, Attributes attributes, long timestamp, TimeUnit unit) { + eventLogger.warn("The addEvent API is not supported at the moment"); + return this; + } + + @Override + public Span setStatus(StatusCode statusCode, String description) { + if (span instanceof Transaction) { + Transaction t = (Transaction) span; + switch (statusCode) { + case OK: + t.withResultIfUnset("OK"); + break; + case ERROR: + t.withResultIfUnset("Error"); + break; + } + } + switch (statusCode) { + case ERROR: + span.withUserOutcome(Outcome.FAILURE); + break; + case OK: + span.withUserOutcome(Outcome.SUCCESS); + break; + case UNSET: + span.withUserOutcome(Outcome.UNKNOWN); + break; + } + return this; + } + + @Override + public Span recordException(Throwable exception, Attributes additionalAttributes) { + span.captureException(exception); + return this; + } + + @Override + public Span updateName(String name) { + span.withName(name); + return this; + } + + @Override + public void end() { + if (span instanceof Transaction) { + onTransactionEnd((Transaction) span); + } else if (span instanceof co.elastic.apm.agent.impl.transaction.Span) { + onSpanEnd((co.elastic.apm.agent.impl.transaction.Span) span); + } + + span.end(); + } + + + private void onTransactionEnd(Transaction t) { + + Map attributes = span.getOtelAttributes(); + boolean isRpc = attributes.containsKey("rpc.system"); + boolean isHttp = attributes.containsKey("http.url") || attributes.containsKey("http.scheme"); + boolean isMessaging = attributes.containsKey("messaging.system"); + String type = "unknown"; + if (span.getOtelKind() == OTelSpanKind.SERVER && (isRpc || isHttp)) { + type = "request"; + } else if (span.getOtelKind() == OTelSpanKind.CONSUMER && isMessaging) { + type = "messaging"; + } + + t.withType(type); + + t.setFrameworkName("OpenTelemetry"); + t.setFrameworkVersion(VersionUtils.getVersion(OpenTelemetry.class, "io.opentelemetry", "opentelemetry-api")); + } + + private void onSpanEnd(co.elastic.apm.agent.impl.transaction.Span s) { + + Map attributes = s.getOtelAttributes(); + + String type = null; + String subType = null; + StringBuilder destinationResource = s.getContext().getDestination().getService().getResource(); + + String netPeerIp = (String) attributes.get("net.peer.ip"); + String netPeerName = (String) attributes.get("net.peer.name"); + Long netPortLong = (Long) attributes.get("net.peer.port"); + int netPort = -1; + if (null != netPortLong && netPortLong > 0L) { + netPort = netPortLong.intValue(); + } + + String netPeer = netPeerName != null ? netPeerName : netPeerIp; + + String httpUrl = (String) attributes.get("http.url"); + String httpScheme = (String) attributes.get("http.scheme"); + String dbSystem = (String) attributes.get("db.system"); + String messagingSystem = (String) attributes.get("messaging.system"); + String rpcSystem = (String) attributes.get("rpc.system"); + if (null != dbSystem) { + type = "db"; + subType = dbSystem; + String dbName = (String) attributes.get("db.name"); + setSpanResource(destinationResource, netPeer, netPort, dbSystem, dbName); + } else if (messagingSystem != null) { + type = "messaging"; + subType = messagingSystem; + String messagingDestination = (String) attributes.get("messaging.destination"); + URI messagingUri = parseURI((String) attributes.get("messaging.url")); + + if (netPeer == null && messagingUri != null) { + netPeer = messagingUri.getHost(); + netPort = messagingUri.getPort(); + } + setSpanResource(destinationResource, netPeer, netPort, messagingSystem, messagingDestination); + } else if (rpcSystem != null) { + type = "external"; + subType = rpcSystem; + String service = (String) attributes.get("rpc.service"); + + setSpanResource(destinationResource, netPeer, netPort, rpcSystem, service); + + } else if (httpUrl != null || httpScheme != null) { + type = "external"; + subType = "http"; + + String httpHost = (String) attributes.get("http.host"); + if (null == httpHost) { + httpHost = netPeer; + } + if (httpHost == null && httpUrl != null) { + URI httpUri = parseURI(httpUrl); + if (httpUri != null) { + httpHost = httpUri.getHost(); + netPort = httpUri.getPort(); + httpScheme = httpUri.getScheme(); + } + } + + netPort = Url.normalizePort(netPort, httpScheme); + + setSpanResource(destinationResource, httpHost, netPort, null, null); + } + + if (type == null) { + type = "unknown"; + if (s.getOtelKind() == OTelSpanKind.INTERNAL) { + type = "app"; + subType = "internal"; + } + } + + s.withType(type).withSubtype(subType); + } + + @Nullable + private static URI parseURI(@Nullable String s) { + if (null == s) { + return null; + } + try { + return new URI(s); + } catch (URISyntaxException e) { + return null; + } + } + + private static void setSpanResource(StringBuilder resource, + @Nullable String netPeer, + int netPort, + @Nullable String system, + @Nullable String suffix) { + + boolean allowSuffix = false; + if (netPeer == null && system != null) { + resource.append(system); + allowSuffix = true; + } else if (netPeer != null) { + resource.append(netPeer); + allowSuffix = true; + if (netPort > 0) { + resource.append(':').append(netPort); + } + } + + if (allowSuffix && suffix != null) { + resource.append('/').append(suffix); + } + } + + @Override + public void end(long timestamp, TimeUnit unit) { + span.end(unit.toMicros(timestamp)); + } + + @Override + public SpanContext getSpanContext() { + return new OTelSpanContext(span.getTraceContext()); + } + + @Override + public boolean isRecording() { + return span.isSampled(); + } + + public AbstractSpan getInternalSpan() { + return span; + } + + @Override + public String toString() { + return "OtelSpan[" + span + "]"; + } +} diff --git a/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/sdk/OTelSpanBuilder.java b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/sdk/OTelSpanBuilder.java new file mode 100644 index 0000000000..dc0066949b --- /dev/null +++ b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/sdk/OTelSpanBuilder.java @@ -0,0 +1,190 @@ +/* + * 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.opentelemetry.sdk; + +import co.elastic.apm.agent.impl.ElasticApmTracer; +import co.elastic.apm.agent.impl.transaction.AbstractSpan; +import co.elastic.apm.agent.impl.transaction.MultiValueMapAccessor; +import co.elastic.apm.agent.impl.transaction.OTelSpanKind; +import co.elastic.apm.agent.impl.transaction.Outcome; +import co.elastic.apm.agent.impl.transaction.Transaction; +import co.elastic.apm.agent.sdk.logging.Logger; +import co.elastic.apm.agent.sdk.logging.LoggerFactory; +import co.elastic.apm.agent.util.LoggerUtils; +import co.elastic.apm.agent.util.PotentiallyMultiValuedMap; +import co.elastic.apm.agent.util.VersionUtils; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanBuilder; +import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; +import io.opentelemetry.context.Context; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +class OTelSpanBuilder implements SpanBuilder { + + private static final Logger addLinkLogger = LoggerUtils.logOnce(LoggerFactory.getLogger(OTelSpanBuilder.class)); + + private final String spanName; + private final ElasticApmTracer elasticApmTracer; + private final Map, Object> attributes = new HashMap<>(); + private long epochMicros = -1; + @Nullable + private AbstractSpan parent; + @Nullable + private Context remoteContext; + + @Nullable + private SpanKind kind; + + public OTelSpanBuilder(String spanName, ElasticApmTracer elasticApmTracer) { + this.spanName = spanName; + this.elasticApmTracer = elasticApmTracer; + } + + @Override + public SpanBuilder setParent(Context context) { + Span span = Span.fromContext(context); + if (span.getSpanContext().isRemote()) { + remoteContext = context; + } else if (span instanceof OTelSpan) { + parent = ((OTelSpan) span).getInternalSpan(); + } + return this; + } + + @Override + public SpanBuilder setNoParent() { + parent = null; + remoteContext = null; + return this; + } + + @Override + public SpanBuilder addLink(SpanContext spanContext) { + addLinkLogger.warn("The addLink API is not supported at the moment"); + return this; + } + + @Override + public SpanBuilder addLink(SpanContext spanContext, Attributes attributes) { + addLinkLogger.warn("The addLink API is not supported at the moment"); + return this; + } + + @Override + public SpanBuilder setAttribute(String key, @Nonnull String value) { + setAttribute(AttributeKey.stringKey(key), value); + return this; + } + + @Override + public SpanBuilder setAttribute(String key, long value) { + setAttribute(AttributeKey.longKey(key), value); + return this; + } + + @Override + public SpanBuilder setAttribute(String key, double value) { + setAttribute(AttributeKey.doubleKey(key), value); + return this; + } + + @Override + public SpanBuilder setAttribute(String key, boolean value) { + setAttribute(AttributeKey.booleanKey(key), value); + return this; + } + + @Override + public SpanBuilder setAttribute(AttributeKey key, @Nonnull T value) { + attributes.put(key, value); + return this; + } + + @Override + public SpanBuilder setSpanKind(SpanKind spanKind) { + kind = spanKind; + return this; + } + + @Override + public SpanBuilder setStartTimestamp(long startTimestamp, TimeUnit unit) { + this.epochMicros = unit.toMicros(startTimestamp); + return this; + } + + @Override + public Span startSpan() { + AbstractSpan span; + + if (parent == null) { + // when parent is not explicitly set, the currently active parent is used as fallback + parent = elasticApmTracer.getActive(); + } + + if (remoteContext != null) { + PotentiallyMultiValuedMap headers = new PotentiallyMultiValuedMap(2); + W3CTraceContextPropagator.getInstance().inject(remoteContext, headers, PotentiallyMultiValuedMap::add); + span = elasticApmTracer.startChildTransaction(headers, MultiValueMapAccessor.INSTANCE, getClass().getClassLoader(), epochMicros); + } else if (parent == null) { + span = elasticApmTracer.startRootTransaction(getClass().getClassLoader(), epochMicros); + } else { + span = elasticApmTracer.startSpan(parent, epochMicros); + } + if (span == null) { + return Span.getInvalid(); + } + span.withName(spanName); + + if (span instanceof Transaction) { + Transaction t = ((Transaction) span); + t.setFrameworkName("OpenTelemetry API"); + + String otelVersion = VersionUtils.getVersion(OpenTelemetry.class, "io.opentelemetry", "opentelemetry-api"); + if(otelVersion != null){ + t.setFrameworkVersion(otelVersion); + } + } + + if (kind == null) { + span.withOtelKind(OTelSpanKind.INTERNAL); + } else { + span.withOtelKind(OTelSpanKind.valueOf(kind.name())); + } + + + // With OTel API, the status (bridged to outcome) should only be explicitly set, thus we have to set and use + // user outcome to provide higher priority and avoid inferring outcome from any reported exception + span.withUserOutcome(Outcome.UNKNOWN); + + OTelSpan otelSpan = new OTelSpan(span); + attributes.forEach((AttributeKey k, Object v) -> otelSpan.setAttribute((AttributeKey) k, (Object) v)); + return otelSpan; + } + +} diff --git a/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/sdk/OTelSpanContext.java b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/sdk/OTelSpanContext.java new file mode 100644 index 0000000000..15f5c99eb7 --- /dev/null +++ b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/sdk/OTelSpanContext.java @@ -0,0 +1,77 @@ +/* + * 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.opentelemetry.sdk; + +import co.elastic.apm.agent.impl.transaction.TraceContext; +import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.api.trace.TraceFlags; +import io.opentelemetry.api.trace.TraceState; +import io.opentelemetry.api.trace.TraceStateBuilder; +import org.stagemonitor.util.StringUtils; + +import java.util.List; + +public class OTelSpanContext implements SpanContext { + private final TraceContext traceContext; + + public OTelSpanContext(TraceContext traceContext) { + this.traceContext = traceContext; + } + + @Override + public String getTraceId() { + return traceContext.getTraceId().toString(); + } + + @Override + public String getSpanId() { + return traceContext.getId().toString(); + } + + @Override + public TraceFlags getTraceFlags() { + return TraceFlags.fromByte(traceContext.getFlags()); + } + + @Override + public TraceState getTraceState() { + // Lazily parses tracestate header. + // Our internal TraceState class doesn't parse the raw tracestate header + // as we currently don't have a use case where the agent needs to read the tracestate. + TraceStateBuilder builder = TraceState.builder(); + List tracestate = traceContext.getTraceState().getTracestate(); + for (int i = 0, size = tracestate.size(); i < size; i++) { + for (String vendorEntry : StringUtils.split(tracestate.get(i), ',')) { + String[] keyValue = StringUtils.split(vendorEntry, '='); + if (keyValue.length == 2) { + builder.put(keyValue[0], keyValue[1]); + } + } + } + return builder.build(); + } + + @Override + public boolean isRemote() { + // the elastic agent doesn't create a TraceContext for remote parents + // instead, it directly creates an entry child span given the request headers + return false; + } + +} diff --git a/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/sdk/OTelTracer.java b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/sdk/OTelTracer.java new file mode 100644 index 0000000000..db800afc1b --- /dev/null +++ b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/sdk/OTelTracer.java @@ -0,0 +1,36 @@ +/* + * 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.opentelemetry.sdk; + +import co.elastic.apm.agent.impl.ElasticApmTracer; +import io.opentelemetry.api.trace.SpanBuilder; +import io.opentelemetry.api.trace.Tracer; + +public class OTelTracer implements Tracer { + private final ElasticApmTracer elasticApmTracer; + + public OTelTracer(ElasticApmTracer elasticApmTracer) { + this.elasticApmTracer = elasticApmTracer; + } + + @Override + public SpanBuilder spanBuilder(String spanName) { + return new OTelSpanBuilder(spanName, elasticApmTracer); + } +} diff --git a/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/sdk/OTelTracerProvider.java b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/sdk/OTelTracerProvider.java new file mode 100644 index 0000000000..c7595e6ff3 --- /dev/null +++ b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/sdk/OTelTracerProvider.java @@ -0,0 +1,42 @@ +/* + * 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.opentelemetry.sdk; + +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.api.trace.TracerProvider; + +import javax.annotation.Nullable; + +public class OTelTracerProvider implements TracerProvider { + private final Tracer tracer; + + public OTelTracerProvider(Tracer tracer) { + this.tracer = tracer; + } + + @Override + public Tracer get(String instrumentationName) { + return get(instrumentationName, null); + } + + @Override + public Tracer get(String instrumentationName, @Nullable String instrumentationVersion) { + return this.tracer; + } +} diff --git a/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/sdk/package-info.java b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/sdk/package-info.java new file mode 100644 index 0000000000..b524bcacb2 --- /dev/null +++ b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/java/co/elastic/apm/agent/opentelemetry/sdk/package-info.java @@ -0,0 +1,22 @@ +/* + * 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. + */ +@NonnullApi +package co.elastic.apm.agent.opentelemetry.sdk; + +import co.elastic.apm.agent.sdk.NonnullApi; diff --git a/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation new file mode 100644 index 0000000000..784affed33 --- /dev/null +++ b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/main/resources/META-INF/services/co.elastic.apm.agent.sdk.ElasticApmInstrumentation @@ -0,0 +1,3 @@ +co.elastic.apm.agent.opentelemetry.GlobalOpenTelemetryInstrumentation +co.elastic.apm.agent.opentelemetry.ContextStorageInstrumentation +co.elastic.apm.agent.opentelemetry.ArrayBasedContextInstrumentation diff --git a/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/test/java/co/elastic/apm/agent/opentelemetry/sdk/ElasticOpenTelemetryTest.java b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/test/java/co/elastic/apm/agent/opentelemetry/sdk/ElasticOpenTelemetryTest.java new file mode 100644 index 0000000000..a90ff9b94b --- /dev/null +++ b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/test/java/co/elastic/apm/agent/opentelemetry/sdk/ElasticOpenTelemetryTest.java @@ -0,0 +1,599 @@ +/* + * 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.opentelemetry.sdk; + +import co.elastic.apm.agent.AbstractInstrumentationTest; +import co.elastic.apm.agent.impl.transaction.AbstractSpan; +import co.elastic.apm.agent.impl.transaction.ElasticContext; +import co.elastic.apm.agent.impl.transaction.OTelSpanKind; +import co.elastic.apm.agent.impl.transaction.Outcome; +import co.elastic.apm.agent.impl.transaction.Transaction; +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.api.trace.Tracer; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.ContextKey; +import io.opentelemetry.context.Scope; +import io.opentelemetry.context.propagation.TextMapGetter; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import javax.annotation.Nullable; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ElasticOpenTelemetryTest extends AbstractInstrumentationTest { + + private OpenTelemetry openTelemetry; + private Tracer otelTracer; + + @Before + public void setUp() { + this.openTelemetry = GlobalOpenTelemetry.get(); + assertThat(openTelemetry).isSameAs(GlobalOpenTelemetry.get()); + otelTracer = openTelemetry.getTracer(null); + + // otel spans are not recycled for now + disableRecyclingValidation(); + + // otel spans should have unknown outcome by default unless explicitly set through API + reporter.disableCheckUnknownOutcome(); + } + + @Before + public void before() { + checkNoActiveContext(); + } + + @After + public void after() { + checkNoActiveContext(); + } + + @Test + public void testTransaction() { + otelTracer.spanBuilder("transaction") + .startSpan() + .end(); + assertThat(reporter.getTransactions()).hasSize(1); + Transaction transaction = reporter.getFirstTransaction(); + assertThat(transaction.getNameAsString()).isEqualTo("transaction"); + + assertThat(transaction.getOtelKind()) + .describedAs("default span kind should be internal when not set explicitly") + .isEqualTo(OTelSpanKind.INTERNAL); + } + + @Test + public void testTransactionWithAttribute() { + otelTracer.spanBuilder("transaction") + .setAttribute("boolean", true) + .setAttribute("long", 42L) + .setAttribute("double", 73D) + .setAttribute("string", "hello") + .startSpan() + .end(); + + assertThat(reporter.getTransactions()).hasSize(1); + Transaction transaction = reporter.getFirstTransaction(); + + assertThat(transaction.getOtelAttributes().get("boolean")).isEqualTo(true); + assertThat(transaction.getOtelAttributes().get("long")).isEqualTo(42L); + assertThat(transaction.getOtelAttributes().get("double")).isEqualTo(73D); + assertThat(transaction.getOtelAttributes().get("string")).isEqualTo("hello"); + } + + @Test + public void testTransactionWithSpanManualPropagation() { + Span transaction = otelTracer.spanBuilder("transaction") + .startSpan(); + otelTracer.spanBuilder("span") + .setParent(Context.root().with(transaction)) + .startSpan() + .end(); + transaction.end(); + + assertThat(reporter.getTransactions()).hasSize(1); + assertThat(reporter.getSpans()).hasSize(1); + Transaction reportedTransaction = reporter.getFirstTransaction(); + assertThat(reportedTransaction.getNameAsString()).isEqualTo("transaction"); + + assertThat(reporter.getFirstSpan().getNameAsString()).isEqualTo("span"); + assertThat(reporter.getFirstSpan().isChildOf(reporter.getFirstTransaction())).isTrue(); + } + + @Test + public void testTransactionWithSpanContextStorePropagation() { + Span transaction = otelTracer.spanBuilder("transaction") + .startSpan(); + try (Scope scope = transaction.makeCurrent()) { + otelTracer.spanBuilder("span") + .startSpan() + .end(); + } finally { + transaction.end(); + } + + assertThat(reporter.getTransactions()).hasSize(1); + assertThat(reporter.getSpans()).hasSize(1); + assertThat(reporter.getFirstTransaction().getNameAsString()).isEqualTo("transaction"); + assertThat(reporter.getFirstSpan().getNameAsString()).isEqualTo("span"); + assertThat(reporter.getFirstSpan().isChildOf(reporter.getFirstTransaction())).isTrue(); + } + + @Test + public void testStartChildAfterEnd() { + Span transaction = otelTracer.spanBuilder("transaction") + .startSpan(); + transaction.end(); + + assertThat(reporter.getTransactions()).hasSize(1); + // simulate reporting the span + reporter.getFirstTransaction().decrementReferences(); + + try (Scope scope = transaction.makeCurrent()) { + otelTracer.spanBuilder("span") + .startSpan() + .end(); + } + assertThat(reporter.getFirstSpan().isChildOf(reporter.getFirstTransaction())).isTrue(); + } + + @Test + public void testTransactionWithRemoteParent() { + Context context = openTelemetry.getPropagators() + .getTextMapPropagator() + .extract(Context.current(), + Map.of("traceparent", "00-cafebabe16cd43dd8448eb211c80319c-deadbeef197918e1-01"), + new MapGetter()); + otelTracer.spanBuilder("transaction") + .setParent(context) + .startSpan() + .end(); + assertThat(reporter.getTransactions()).hasSize(1); + assertThat(reporter.getFirstTransaction().getNameAsString()).isEqualTo("transaction"); + assertThat(reporter.getFirstTransaction().getTraceContext().getTraceId().toString()).isEqualTo("cafebabe16cd43dd8448eb211c80319c"); + assertThat(reporter.getFirstTransaction().getTraceContext().getParentId().toString()).isEqualTo("deadbeef197918e1"); + } + + @Test + public void testTransactionInject() { + Span transaction = otelTracer.spanBuilder("transaction") + .startSpan(); + HashMap otelHeaders = new HashMap<>(); + HashMap elasticApmHeaders = new HashMap<>(); + try (Scope scope = transaction.makeCurrent()) { + openTelemetry.getPropagators().getTextMapPropagator().inject(Context.current(), otelHeaders, HashMap::put); + tracer.getActive().propagateTraceContext(elasticApmHeaders, (k, v, m) -> m.put(k, v)); + } finally { + transaction.end(); + } + assertThat(elasticApmHeaders).containsAllEntriesOf(otelHeaders); + } + + @Test + public void setStartTimestamp() { + + Instant transactionStart = Instant.now(); + + otelTracer.spanBuilder("transaction") + .setStartTimestamp(transactionStart) + .startSpan() + // for test reliability we have to provide explicit end time + // otherwise since OTel time is in nano-seconds and stored in micro-seconds there are not-so rare cases + // where the computed duration appears negative. + .end(transactionStart); + + long transactionStartMicros = ChronoUnit.MICROS.between(Instant.EPOCH, transactionStart); + + assertThat(reporter.getTransactions()).hasSize(1); + assertThat(reporter.getFirstTransaction().getTimestamp()).isEqualTo(transactionStartMicros); + } + + @Test + public void otelBridgedRootContext() { + checkBridgedContext(Context.root()); + + assertThat(Context.root()) + .describedAs("wrapped root context should be current") + .isSameAs(Context.current()); + } + + public ElasticContext checkBridgedContext(Context context) { + assertThat(context).isInstanceOf(ElasticContext.class); + + // we have to check class name as the wrapper class is loaded in the plugin CL and it is also loadable from + // the current CL, thus making class equality not work as expected + assertThat(context.getClass().getName()) + .describedAs("root context should be wrapped") + .doesNotStartWith("io.opentelemetry"); + + return (ElasticContext) context; + } + + @Test + public void otelContextStoreAndRetrieve() { + Span span = otelTracer.spanBuilder("span").startSpan(); + + checkNoActiveContext(); + + ContextKey key1 = ContextKey.named("key1"); + Context context1 = Context.current().with(key1, "value1"); + assertThat(context1.get(key1)).isEqualTo("value1"); + + ContextKey key2 = ContextKey.named("key2"); + + try (Scope scope1 = context1.makeCurrent()) { + checkCurrentContext(context1, "first context should be active"); + checkCurrentContextKey(key1, "value1"); + + Context context2 = context1.with(key2, "value2"); + try (Scope scope2 = context2.makeCurrent()) { + checkCurrentContext(context2, "second context should be active"); + checkCurrentContextKey(key1, "value1"); + checkCurrentContextKey(key2, "value2"); + + try (Scope spanScope = span.makeCurrent()) { + assertThat(Context.current()) + .describedAs("span context should have its own context") + .isNotSameAs(context2) + .isNotSameAs(context1); + checkCurrentContextKey(key1, "value1"); + checkCurrentContextKey(key2, "value2"); + } finally { + span.end(); + } + checkCurrentContext(context2, "second context should be restored"); + } + + assertThat(context1.get(key2)) + .describedAs("context should be immutable") + .isNull(); + + checkCurrentContext(context1, "context should be restored"); + } + + checkNoActiveContext(); + + } + + private static void checkCurrentContext(Context expected, String assertMsg) { + assertThat(Context.current()) + .describedAs(assertMsg) + .isSameAs(expected); + + assertThat(expected) + .describedAs("otel context should also be an elastic context") + .isInstanceOf(ElasticContext.class); + + assertThat(tracer.currentContext()) + .describedAs(assertMsg) + .isSameAs(expected); + } + + @Test + public void otelContextRetrieveByKeyReferenceOnly() { + ContextKey key = ContextKey.named("key"); + Context contextWithKey = Context.current().with(key, "value"); + assertThat(contextWithKey.get(key)).isEqualTo("value"); + + String valueWithSameNameKey = contextWithKey.get(ContextKey.named("key")); + assertThat(valueWithSameNameKey) + .describedAs("only key reference should allow to get values in context") + .isNull(); + } + + @Test + public void otelContextRootIdempotent() { + assertThat(Context.root()) + .describedAs("multiple calls to Context.root() should return the same value") + .isSameAs(Context.root()); + } + + @Test + public void otelContextCurrentIdempotent() { + + // create a non-root context by adding a value + ContextKey key = ContextKey.named("key"); + Context context = Context.current().with(key, "value"); + + try (Scope scope = context.makeCurrent()) { + assertThat(context).isNotSameAs(Context.root()); + checkCurrentContext(context, "multiple calls to Context.current() should return the same value"); + checkCurrentContextKey(key, "value"); + } + + assertThat(Context.current().get(key)).isNull(); + } + + @Test + public void otelContextMakeCurrentMoreThanOnce() { + ContextKey key = ContextKey.named("key"); + Context context = Context.current().with(key, "value"); + + try (Scope scope1 = context.makeCurrent()) { + assertThat(scope1).isNotSameAs(Scope.noop()); + checkCurrentContext(context, "first activation should activate context"); + + // here the context is expected to remain the same as it is not modified (we don't add any value to it) + try (Scope scope2 = context.makeCurrent()) { + checkCurrentContext(context, "double activation should keep the same context"); + assertThat(scope2) + .describedAs("nested scope should be noop as context remains the same") + .isSameAs(Scope.noop()); + } + } + } + + @Test + public void contextActivationFromElastic() { + ContextKey key = ContextKey.named("key"); + + Context context = Context.root().with(key, "value"); + ElasticContext bridgedContext = checkBridgedContext(context); + + // activate context from elastic API using a bridged context + try (co.elastic.apm.agent.impl.Scope scope = bridgedContext.activateInScope()) { + + checkCurrentContext(context, "elastic and otel contexts should be the same"); + + checkCurrentContextKey(key, "value"); + + } + } + + + @Test + public void contextActivationFromOtel() { + + ContextKey key = ContextKey.named("key"); + Context context = Context.root().with(key, "value"); + + checkBridgedContext(context); + + // activate context from Otel API + try (Scope scope = context.makeCurrent()) { + + checkCurrentContext(context, "elastic and otel contexts should be the same"); + + checkCurrentContextKey(key, "value"); + } + + } + + private void checkCurrentContextKey(ContextKey key, String expectedValue) { + Context current = Context.current(); + assertThat(current.get(key)) + .describedAs("context %s should contain %s=%s", current, key, expectedValue) + .isEqualTo(expectedValue); + } + + private void checkNoActiveContext() { + assertThat(tracer.currentContext()) + .describedAs("no active elastic context is expected") + .isNull(); + assertThat(Context.current()) + .describedAs("no active otel context is expected") + .isSameAs(Context.root()) + .isNotNull(); + } + + @Test + public void otelStateWithActiveElasticTransaction() { + + Transaction transaction = startTestRootTransaction(); + + try { + assertThat(tracer.currentContext()).isSameAs(transaction); + + assertThat(Context.current()) + .describedAs("current otel context should have elastic span as active") + .isNotSameAs(Context.root()); + + assertThat(Span.current()) + .describedAs("elastic span should appear visible in current context") + .isNotNull(); + + assertThat(tracer.currentContext()) + .describedAs("current context should have been upgraded to otel context") + .isNotNull() + .isNotSameAs(transaction); + + assertThat(tracer.currentTransaction()) + .isSameAs(tracer.currentContext().getSpan()) + .isSameAs(transaction); + } finally { + // this must transparently deactivate the upgraded context + transaction.deactivate().end(); + } + } + + @Test + public void otelSpanOverActiveElasticTransaction() { + Transaction transaction = startTestRootTransaction(); + + String spanId; + try { + + Span otelSpan = openTelemetry.getTracer("test") + .spanBuilder("otel span") + .startSpan(); + + try (Scope scope = otelSpan.makeCurrent()) { + spanId = Span.current().getSpanContext().getSpanId(); + } + + otelSpan.end(); + + } finally { + transaction.deactivate().end(); + } + + assertThat(reporter.getNumReportedTransactions()).isEqualTo(1); + assertThat(reporter.getFirstTransaction()).isSameAs(transaction); + + assertThat(reporter.getNumReportedSpans()).isEqualTo(1); + AbstractSpan reportedSpan = reporter.getFirstSpan().getSpan(); + assertThat(reportedSpan).isNotNull(); + assertThat(reportedSpan.getNameAsString()).isEqualTo("otel span"); + assertThat(reportedSpan.getTraceContext().getId().toString()).isEqualTo(spanId); + assertThat(reportedSpan.getTraceContext().isChildOf(transaction.getTraceContext())).isTrue(); + } + + @Test + public void elasticSpanOverOtelSpan() { + // create and activate an otel span which should create a transaction + // create and activate an elastic span + + Span otelSpan = openTelemetry.getTracer("test") + .spanBuilder("otel transaction") + .startSpan(); + + Transaction transaction; + try (Scope scope = otelSpan.makeCurrent()) { + + transaction = tracer.currentTransaction(); + assertThat(transaction).isNotNull(); + + co.elastic.apm.agent.impl.transaction.Span elasticSpan = transaction.createSpan(); + try (co.elastic.apm.agent.impl.Scope elasticScope = elasticSpan.activateInScope()) { + assertThat(tracer.getActiveSpan()).isNotNull(); + tracer.getActiveSpan().withName("elastic span"); + } finally { + elasticSpan.end(); + } + } finally { + otelSpan.end(); + } + + assertThat(reporter.getNumReportedTransactions()).isEqualTo(1); + assertThat(reporter.getFirstTransaction()).isSameAs(transaction); + assertThat(transaction.getNameAsString()).isEqualTo("otel transaction"); + + assertThat(reporter.getNumReportedSpans()).isEqualTo(1); + AbstractSpan reportedSpan = reporter.getFirstSpan().getSpan(); + assertThat(reportedSpan).isNotNull(); + assertThat(reportedSpan.getNameAsString()).isEqualTo("elastic span"); + assertThat(reportedSpan.getTraceContext().isChildOf(transaction.getTraceContext())).isTrue(); + } + + @Test + public void testOTelSpanAttributesCopiedAsIs() { + otelTracer.spanBuilder("transaction").setSpanKind(SpanKind.SERVER) + .startSpan() + .setAttribute(SemanticAttributes.HTTP_METHOD, "GET") + .setAttribute(SemanticAttributes.HTTP_URL, "http://example.com:8080/foo?bar") + .setAttribute(SemanticAttributes.HTTP_STATUS_CODE, 200L) + .setAttribute(SemanticAttributes.NET_PEER_PORT, 123456) + .setAttribute(SemanticAttributes.NET_PEER_IP, "192.168.178.1") + .end(); + assertThat(reporter.getTransactions()).hasSize(1); + + checkOTelAttributes(reporter.getFirstTransaction(), Map.of( + SemanticAttributes.HTTP_METHOD.getKey(), "GET", + SemanticAttributes.HTTP_URL.getKey(), "http://example.com:8080/foo?bar", + SemanticAttributes.HTTP_STATUS_CODE.getKey(), 200L, + SemanticAttributes.NET_PEER_PORT.getKey(), 123456L, + SemanticAttributes.NET_PEER_IP.getKey(), "192.168.178.1" + )); + } + + private static void checkOTelAttributes(AbstractSpan context, Map expected) { + assertThat(context.getOtelAttributes()) + .containsAllEntriesOf(expected) + .hasSameSizeAs(expected); + } + + @Test + public void reportErrorKeepsUnknownOutcome() { + checkReportError(Outcome.UNKNOWN, span -> { + span.recordException(new IllegalStateException()); + }); + } + + @Test + public void reportErrorAndSetStatusOk() { + checkReportError(Outcome.SUCCESS, span -> { + span.setStatus(StatusCode.OK); + span.recordException(new IllegalStateException()); + }); + } + + @Test + public void reportErrorAndSetStatusError() { + checkReportError(Outcome.FAILURE, span -> { + span.recordException(new IllegalStateException()); + span.setStatus(StatusCode.ERROR); + }); + } + + private void checkReportError(Outcome expectedOutcome, Consumer actions) { + + Span transaction = otelTracer.spanBuilder("transaction with error") + .startSpan(); + + try (Scope scope = transaction.makeCurrent()) { + actions.accept(transaction); + + Span span = otelTracer.spanBuilder("span with error") + .startSpan(); + + actions.accept(span); + + span.end(); + } finally { + transaction.end(); + } + + assertThat(reporter.getTransactions()).hasSize(1); + assertThat(reporter.getSpans()).hasSize(1); + + assertThat(reporter.getErrors()).hasSize(2); + + assertThat(reporter.getFirstTransaction().getOutcome()) + .describedAs("recording an exception in transaction should not alter outcome") + .isEqualTo(expectedOutcome); + assertThat(reporter.getFirstSpan().getOutcome()) + .describedAs("recording an exception in span should not alter outcome") + .isEqualTo(expectedOutcome); + } + + public static class MapGetter implements TextMapGetter> { + @Override + public Iterable keys(Map carrier) { + return carrier.keySet(); + } + + @Nullable + @Override + public String get(@Nullable Map carrier, String key) { + return carrier.get(key); + } + } +} diff --git a/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/test/java/co/elastic/apm/agent/opentelemetry/sdk/OTelSpanKindTest.java b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/test/java/co/elastic/apm/agent/opentelemetry/sdk/OTelSpanKindTest.java new file mode 100644 index 0000000000..ad6c14da33 --- /dev/null +++ b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/test/java/co/elastic/apm/agent/opentelemetry/sdk/OTelSpanKindTest.java @@ -0,0 +1,38 @@ +/* + * 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.opentelemetry.sdk; + +import co.elastic.apm.agent.impl.transaction.OTelSpanKind; +import io.opentelemetry.api.trace.SpanKind; +import org.junit.jupiter.api.Test; + +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; + +public class OTelSpanKindTest { + + @Test + void checkSpanKindMapping() { + assertThat(Stream.of(OTelSpanKind.values()).map(Enum::name).collect(Collectors.toSet())) + .containsExactlyElementsOf(Stream.of(SpanKind.values()).map(Enum::name).collect(Collectors.toSet())); + + } +} diff --git a/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/test/java/specs/OTelBridgeCucumberTest.java b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/test/java/specs/OTelBridgeCucumberTest.java new file mode 100644 index 0000000000..64d83a3bce --- /dev/null +++ b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/test/java/specs/OTelBridgeCucumberTest.java @@ -0,0 +1,29 @@ +/* + * 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 specs; + +import io.cucumber.junit.Cucumber; +import io.cucumber.junit.CucumberOptions; +import org.junit.runner.RunWith; + +@RunWith(Cucumber.class) +@CucumberOptions(strict = true, plugin = {"pretty"}, tags = "@opentelemetry-bridge") +public class OTelBridgeCucumberTest { + +} diff --git a/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/test/java/specs/OTelBridgeStepsDefinitions.java b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/test/java/specs/OTelBridgeStepsDefinitions.java new file mode 100644 index 0000000000..f380a6668a --- /dev/null +++ b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-plugin/src/test/java/specs/OTelBridgeStepsDefinitions.java @@ -0,0 +1,341 @@ +/* + * 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 specs; + +import co.elastic.apm.agent.impl.Scope; +import co.elastic.apm.agent.impl.transaction.AbstractSpan; +import co.elastic.apm.agent.impl.transaction.OTelSpanKind; +import co.elastic.apm.agent.impl.transaction.Span; +import co.elastic.apm.agent.impl.transaction.TraceContext; +import co.elastic.apm.agent.impl.transaction.Transaction; +import co.elastic.apm.agent.opentelemetry.sdk.ElasticOpenTelemetry; +import co.elastic.apm.agent.opentelemetry.sdk.ElasticOpenTelemetryTest; +import co.elastic.apm.agent.opentelemetry.sdk.OTelSpan; +import io.cucumber.java.Before; +import io.cucumber.java.en.Given; +import io.cucumber.java.en.Then; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.trace.SpanBuilder; +import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.context.Context; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; + +import javax.annotation.Nullable; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.function.Function; + +import static org.assertj.core.api.Assertions.assertThat; + +public class OTelBridgeStepsDefinitions { + + private static final String REMOTE_PARENT_TRACE_ID = "cafebabe16cd43dd8448eb211c80319c"; + private static final String REMOTE_PARENT_ID = "deadbeef197918e1"; + + // due to lazy-init access to this should use getOtel() + @Nullable + private ElasticOpenTelemetry otel; + + // state will contain the Elastic state when created before OTel + // this is required for shared steps definitions like 'an active transaction' + private final ScenarioState state; + + private OTelSpan otelSpan; + + private Map otelSpanAttributes; + + private Context localParentContext = null; + + public OTelBridgeStepsDefinitions(ScenarioState state) { + this.state = state; + } + + @Before + public void resetState() { + this.otelSpan = null; + this.localParentContext = null; + this.otelSpanAttributes = new HashMap<>(); + } + + private ElasticOpenTelemetry getOtel() { + // lazily initialize OTel as the tracer state from scenario state + if (otel == null) { + otel = new ElasticOpenTelemetry(state.getTracer()); + } + return otel; + } + + // creating elastic span or transaction from OTel span + + @Given("OTel span is created with remote context as parent") + public void createOTelSpanWithRemoteContext() { + otelSpan = (OTelSpan) getOtel().getTracer("") + .spanBuilder("otel span") + .setParent(getRemoteContext()) + .startSpan(); + + assertThat(otelSpan.getSpanContext().getTraceId()).isEqualTo(REMOTE_PARENT_TRACE_ID); + } + + @Then("Elastic bridged transaction has remote context as parent") + public void bridgedTransactionWithRemoteContextParent() { + TraceContext traceContext = getBridgedTransaction().getTraceContext(); + assertThat(traceContext.isRoot()).isFalse(); + assertThat(traceContext.getParentId().toString()).isEqualTo(REMOTE_PARENT_ID); + assertThat(traceContext.getTraceId().toString()).isEqualTo(REMOTE_PARENT_TRACE_ID); + } + + private Context getRemoteContext(){ + return getOtel().getPropagators() + .getTextMapPropagator() + .extract(Context.current(), + Map.of("traceparent", String.format("00-%s-%s-01", REMOTE_PARENT_TRACE_ID, REMOTE_PARENT_ID), + "tracestate", "k=v"), + new ElasticOpenTelemetryTest.MapGetter()); + } + + @Given("OTel span is created without parent") + public void createOTelSpanWithoutParent(){ + otelSpan = (OTelSpan) getOtel().getTracer("") + .spanBuilder("otel span") + .setNoParent() // redundant, but makes it explicit + .startSpan(); + } + + @Then("Elastic bridged transaction is a root transaction") + public void bridgedTransactionIsRootTransaction() { + TraceContext traceContext = getBridgedTransaction().getTraceContext(); + assertThat(traceContext.isRoot()).isTrue(); + } + + @Given("OTel span is created with local context as parent") + public void createOTelSpanWithLocalParent() { + localParentContext = Context.root().with(getOtel().getTracer("").spanBuilder("parent").startSpan()); + + otelSpan = (OTelSpan) getOtel().getTracer("") + .spanBuilder("otel span") + .setParent(localParentContext) + .startSpan(); + + } + + @Then("Elastic bridged span has local context as parent") + public void bridgedSpanHasLocalParent() { + assertThat(localParentContext).isNotNull(); + + SpanContext otelParentContext = io.opentelemetry.api.trace.Span.fromContext(localParentContext).getSpanContext(); + + TraceContext bridgedSpanContext = getBridgedSpan().getTraceContext(); + assertThat(bridgedSpanContext.getTraceId().toString()).isEqualTo(otelParentContext.getTraceId()); + assertThat(bridgedSpanContext.getParentId().toString()).isEqualTo(otelParentContext.getSpanId()); + } + + // OTel span kind mapping for spans & transactions + + @Given("OTel span is created with kind {string}") + public void otelSpanIsCreatedWithKind(String kind) { + // we have to use a parent transaction as we are creating a span + // the parent transaction is created by another step definition, thus we reuse the existing state + Transaction parentTransaction = state.getTransaction(); + + Function createSpanWithKind = k -> { + SpanBuilder spanBuilder = getOtel().getTracer("") + .spanBuilder("span") + .setSpanKind(SpanKind.valueOf(k)); + return (OTelSpan) spanBuilder.startSpan(); + }; + + if( parentTransaction != null){ + // creating a span as a child of existing transaction + try (Scope scope = parentTransaction.activateInScope()) { + this.otelSpan = createSpanWithKind.apply(kind); + } + } else { + // creating a root transaction + this.otelSpan = createSpanWithKind.apply(kind); + } + + } + + @Given("OTel span has following attributes") + public void otelSpanAttributes(io.cucumber.datatable.DataTable table) { + table.cells().forEach(r -> { + String key = r.get(0); + String stringValue = r.get(1); + if (stringValue != null) { + AttributeKey attributeKey = lookupKey(key); + + Object valueAsObject; + switch (attributeKey.getType()) { + case LONG: + Long longValue = Long.parseLong(stringValue); + otelSpan.setAttribute(attributeKey, longValue); + valueAsObject = longValue; + break; + case BOOLEAN: + Boolean booleanValue = Boolean.parseBoolean(stringValue); + otelSpan.setAttribute(attributeKey, booleanValue); + valueAsObject = booleanValue; + break; + default: + otelSpan.setAttribute(key, stringValue); + valueAsObject = stringValue; + } + otelSpanAttributes.put(key, valueAsObject); + } + }); + } + + private static AttributeKey lookupKey(String name) { + switch (name) { + case "http.url": + return SemanticAttributes.HTTP_URL; + case "http.scheme": + return SemanticAttributes.HTTP_SCHEME; + case "http.host": + return SemanticAttributes.HTTP_HOST; + case "net.peer.name": + return SemanticAttributes.NET_PEER_NAME; + case "net.peer.ip": + return SemanticAttributes.NET_PEER_IP; + case "net.peer.port": + return SemanticAttributes.NET_PEER_PORT; + case "db.system": + return SemanticAttributes.DB_SYSTEM; + case "db.name": + return SemanticAttributes.DB_NAME; + case "messaging.system": + return SemanticAttributes.MESSAGING_SYSTEM; + case "messaging.url": + return SemanticAttributes.MESSAGING_URL; + case "messaging.destination": + return SemanticAttributes.MESSAGING_DESTINATION; + case "rpc.system": + return SemanticAttributes.RPC_SYSTEM; + case "rpc.service": + return SemanticAttributes.RPC_SERVICE; + default: + throw new IllegalArgumentException("unknown key for name " + name); + } + } + + @Then("Elastic bridged (transaction|span) OTel kind is {string}") + public void bridgeObjectKind(String kind){ + assertThat(getBridgedAbstractSpan().getOtelKind()) + .isEqualTo(OTelSpanKind.valueOf(kind)); + } + + @Then("Elastic bridged object is a {contextType}") + public void bridgeSpanType(String type) { + switch (type) { + case "span": + getBridgedSpan(); + break; + case "transaction": + getBridgedTransaction(); + break; + default: + throw new IllegalArgumentException("unknown type " + type); + } + } + + @Then("Elastic bridged {contextType} type is {string}") + public void bridgeObjectType(String contextType, String expected) { + AbstractSpan bridgedObject = getBridgedAbstractSpan(); + String type; + if (bridgedObject instanceof Transaction) { + assertThat(contextType).isEqualTo("transaction"); + type = ((Transaction) bridgedObject).getType(); + } else { + assertThat(contextType).isEqualTo("span"); + type = ((Span) bridgedObject).getType(); + } + + assertThat(type).isEqualTo(expected); + } + + @Then("Elastic bridged span subtype is {string}") + public void bridgeObjectSubtype(String expected) { + if (expected.isEmpty()) { + expected = null; + } + assertThat(getBridgedSpan().getSubtype()).isEqualTo(expected); + } + + @Then("Elastic bridged span OTel attributes are copied as-is") + public void bridgeObjectOTelAttributesCheck() { + assertThat(otelSpan.getInternalSpan().getOtelAttributes()) + .containsExactlyEntriesOf(otelSpanAttributes); + } + + @Then("Elastic bridged span destination resource is not set") + public void bridgeObjectDestinationResourceNotSet() { + assertThat(getDestinationResource()).isEmpty(); + } + + @Then("Elastic bridged span destination resource is set to {string}") + public void bridgeObjectDestinationResource(String expected) { + assertThat(getDestinationResource()) + .describedAs("destination resource expected for otel attributes: %s", getBridgedSpan().getOtelAttributes()) + .isEqualTo(expected); + } + + @Then("Elastic bridged {contextType} outcome is {string}") + public void bridgedObjectOutcome(String ignoredContextType, String outcome) { + assertThat(otelSpan.getInternalSpan().getOutcome()) + .isEqualTo(OutcomeStepsDefinitions.fromString(outcome)); + + } + + @Then("OTel span status set to {string}") + public void setOtelSpanStatus(String status){ + otelSpan.setStatus(StatusCode.valueOf(status.toUpperCase(Locale.ROOT))); + } + + @Given("OTel span ends") + public void otelSpanEnds() { + otelSpan.end(); + } + + private String getDestinationResource() { + return getBridgedSpan().getContext().getDestination().getService().getResource().toString(); + } + + private AbstractSpan getBridgedAbstractSpan() { + return getBridgedObject(AbstractSpan.class); + } + + private Transaction getBridgedTransaction() { + return getBridgedObject(Transaction.class); + } + + private Span getBridgedSpan() { + return getBridgedObject(Span.class); + } + + private > T getBridgedObject(Class expectedType) { + AbstractSpan internalSpan = otelSpan.getInternalSpan(); + assertThat(internalSpan).isInstanceOf(expectedType); + return expectedType.cast(internalSpan); + } + +} diff --git a/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-test/pom.xml b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-test/pom.xml new file mode 100644 index 0000000000..69a948bcea --- /dev/null +++ b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-test/pom.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + + co.elastic.apm + apm-opentelemetry + 1.30.0-SNAPSHOT + + + ${project.groupId}:${project.artifactId} + apm-opentelemetry-test + + + ${project.basedir}/../../.. + + + + + ${project.groupId} + apm-opentelemetry-plugin + test-jar + ${project.version} + test + + + io.opentelemetry + * + + + + + ${project.groupId} + apm-opentelemetry-plugin + ${project.version} + test + + + io.opentelemetry + * + + + + + org.apache.ivy + ivy + test + + + + diff --git a/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-test/src/test/java/co/elastic/apm/opentelemetry/OpenTelemetryVersionIT.java b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-test/src/test/java/co/elastic/apm/opentelemetry/OpenTelemetryVersionIT.java new file mode 100644 index 0000000000..90436cb3d2 --- /dev/null +++ b/apm-agent-plugins/apm-opentelemetry/apm-opentelemetry-test/src/test/java/co/elastic/apm/opentelemetry/OpenTelemetryVersionIT.java @@ -0,0 +1,64 @@ +/* + * 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.opentelemetry; + +import co.elastic.apm.agent.TestClassWithDependencyRunner; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.Arrays; +import java.util.List; + +@RunWith(Parameterized.class) +public class OpenTelemetryVersionIT { + private final TestClassWithDependencyRunner runner; + + public OpenTelemetryVersionIT(String version) throws Exception { + List dependencies = List.of( + "io.opentelemetry:opentelemetry-api:" + version, + "io.opentelemetry:opentelemetry-context:" + version, + "io.opentelemetry:opentelemetry-semconv:" + version + "-alpha"); + runner = new TestClassWithDependencyRunner(dependencies, + "co.elastic.apm.agent.opentelemetry.sdk.ElasticOpenTelemetryTest", + "co.elastic.apm.agent.opentelemetry.sdk.ElasticOpenTelemetryTest$MapGetter"); + } + + @Parameterized.Parameters(name= "{0}") + public static Iterable data() { + return Arrays.asList(new Object[][]{ + {"1.0.1"}, + {"1.1.0"}, + {"1.2.0"}, + {"1.3.0"}, + {"1.4.1"}, + {"1.5.0"}, + {"1.6.0"}, + {"1.7.1"}, + {"1.9.0"}, + {"1.10.1"}, + {"1.11.0"} + }); + } + + @Test + public void testVersions() throws Exception { + runner.run(); + } +} diff --git a/apm-agent-plugins/apm-opentelemetry/pom.xml b/apm-agent-plugins/apm-opentelemetry/pom.xml new file mode 100644 index 0000000000..8fd80d473a --- /dev/null +++ b/apm-agent-plugins/apm-opentelemetry/pom.xml @@ -0,0 +1,30 @@ + + + 4.0.0 + + + co.elastic.apm + apm-agent-plugins + 1.30.0-SNAPSHOT + + + apm-opentelemetry + ${project.groupId}:${project.artifactId} + pom + + + + ${project.basedir}/../.. + + 8 + 8 + true + + + + + apm-opentelemetry-plugin + apm-opentelemetry-test + + + diff --git a/apm-agent-plugins/apm-opentracing-plugin/pom.xml b/apm-agent-plugins/apm-opentracing-plugin/pom.xml index b1523903b1..a99b718065 100644 --- a/apm-agent-plugins/apm-opentracing-plugin/pom.xml +++ b/apm-agent-plugins/apm-opentracing-plugin/pom.xml @@ -5,7 +5,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-opentracing-plugin diff --git a/apm-agent-plugins/apm-process-plugin/pom.xml b/apm-agent-plugins/apm-process-plugin/pom.xml index 181384059b..b195fd47d3 100644 --- a/apm-agent-plugins/apm-process-plugin/pom.xml +++ b/apm-agent-plugins/apm-process-plugin/pom.xml @@ -3,7 +3,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/apm-agent-plugins/apm-profiling-plugin/pom.xml b/apm-agent-plugins/apm-profiling-plugin/pom.xml index e6b5b85971..1697bae89e 100644 --- a/apm-agent-plugins/apm-profiling-plugin/pom.xml +++ b/apm-agent-plugins/apm-profiling-plugin/pom.xml @@ -5,7 +5,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-profiling-plugin diff --git a/apm-agent-plugins/apm-quartz-job-plugin/apm-quartz-1-plugin/pom.xml b/apm-agent-plugins/apm-quartz-job-plugin/apm-quartz-1-plugin/pom.xml index bc8e380a37..809fcb8ba9 100644 --- a/apm-agent-plugins/apm-quartz-job-plugin/apm-quartz-1-plugin/pom.xml +++ b/apm-agent-plugins/apm-quartz-job-plugin/apm-quartz-1-plugin/pom.xml @@ -3,7 +3,7 @@ apm-quartz-job-plugin co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/apm-agent-plugins/apm-quartz-job-plugin/apm-quartz-2-plugin/pom.xml b/apm-agent-plugins/apm-quartz-job-plugin/apm-quartz-2-plugin/pom.xml index 5e0b958ff6..a021d265f6 100644 --- a/apm-agent-plugins/apm-quartz-job-plugin/apm-quartz-2-plugin/pom.xml +++ b/apm-agent-plugins/apm-quartz-job-plugin/apm-quartz-2-plugin/pom.xml @@ -3,7 +3,7 @@ apm-quartz-job-plugin co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/apm-agent-plugins/apm-quartz-job-plugin/pom.xml b/apm-agent-plugins/apm-quartz-job-plugin/pom.xml index e0c213bb38..87a985b00f 100644 --- a/apm-agent-plugins/apm-quartz-job-plugin/pom.xml +++ b/apm-agent-plugins/apm-quartz-job-plugin/pom.xml @@ -11,7 +11,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-quartz-job-plugin diff --git a/apm-agent-plugins/apm-quartz-job-plugin/quartz-common/pom.xml b/apm-agent-plugins/apm-quartz-job-plugin/quartz-common/pom.xml index 46ea522d5f..d45e5becc8 100644 --- a/apm-agent-plugins/apm-quartz-job-plugin/quartz-common/pom.xml +++ b/apm-agent-plugins/apm-quartz-job-plugin/quartz-common/pom.xml @@ -3,7 +3,7 @@ apm-quartz-job-plugin co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/apm-agent-plugins/apm-rabbitmq/apm-rabbitmq-plugin/pom.xml b/apm-agent-plugins/apm-rabbitmq/apm-rabbitmq-plugin/pom.xml index 78709c8884..73c3c07974 100644 --- a/apm-agent-plugins/apm-rabbitmq/apm-rabbitmq-plugin/pom.xml +++ b/apm-agent-plugins/apm-rabbitmq/apm-rabbitmq-plugin/pom.xml @@ -5,7 +5,7 @@ apm-rabbitmq co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-rabbitmq-plugin diff --git a/apm-agent-plugins/apm-rabbitmq/apm-rabbitmq-spring/pom.xml b/apm-agent-plugins/apm-rabbitmq/apm-rabbitmq-spring/pom.xml index 71aaece9cb..23b50ec89e 100644 --- a/apm-agent-plugins/apm-rabbitmq/apm-rabbitmq-spring/pom.xml +++ b/apm-agent-plugins/apm-rabbitmq/apm-rabbitmq-spring/pom.xml @@ -5,7 +5,7 @@ apm-rabbitmq co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-rabbitmq-spring diff --git a/apm-agent-plugins/apm-rabbitmq/apm-rabbitmq-test-3/pom.xml b/apm-agent-plugins/apm-rabbitmq/apm-rabbitmq-test-3/pom.xml index 096dd7d188..81c9e32b2a 100644 --- a/apm-agent-plugins/apm-rabbitmq/apm-rabbitmq-test-3/pom.xml +++ b/apm-agent-plugins/apm-rabbitmq/apm-rabbitmq-test-3/pom.xml @@ -5,7 +5,7 @@ apm-rabbitmq co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-rabbitmq-test-3 diff --git a/apm-agent-plugins/apm-rabbitmq/apm-rabbitmq-test-4/pom.xml b/apm-agent-plugins/apm-rabbitmq/apm-rabbitmq-test-4/pom.xml index f55cb46c4d..01c70ed93f 100644 --- a/apm-agent-plugins/apm-rabbitmq/apm-rabbitmq-test-4/pom.xml +++ b/apm-agent-plugins/apm-rabbitmq/apm-rabbitmq-test-4/pom.xml @@ -5,7 +5,7 @@ apm-rabbitmq co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-rabbitmq-test-4 diff --git a/apm-agent-plugins/apm-rabbitmq/pom.xml b/apm-agent-plugins/apm-rabbitmq/pom.xml index 61f3638e49..03d4441bef 100644 --- a/apm-agent-plugins/apm-rabbitmq/pom.xml +++ b/apm-agent-plugins/apm-rabbitmq/pom.xml @@ -5,7 +5,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-rabbitmq diff --git a/apm-agent-plugins/apm-reactor-plugin/pom.xml b/apm-agent-plugins/apm-reactor-plugin/pom.xml index 87625cb913..730c3127a4 100644 --- a/apm-agent-plugins/apm-reactor-plugin/pom.xml +++ b/apm-agent-plugins/apm-reactor-plugin/pom.xml @@ -5,7 +5,7 @@ co.elastic.apm apm-agent-plugins - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-reactor-plugin diff --git a/apm-agent-plugins/apm-redis-plugin/apm-jedis-2-tests/pom.xml b/apm-agent-plugins/apm-redis-plugin/apm-jedis-2-tests/pom.xml index a3c7aa0a56..16d18a6565 100644 --- a/apm-agent-plugins/apm-redis-plugin/apm-jedis-2-tests/pom.xml +++ b/apm-agent-plugins/apm-redis-plugin/apm-jedis-2-tests/pom.xml @@ -5,7 +5,7 @@ apm-redis-plugin co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-jedis-2-tests diff --git a/apm-agent-plugins/apm-redis-plugin/apm-jedis-3-tests/pom.xml b/apm-agent-plugins/apm-redis-plugin/apm-jedis-3-tests/pom.xml index f3f75c9c2a..2bf392045c 100644 --- a/apm-agent-plugins/apm-redis-plugin/apm-jedis-3-tests/pom.xml +++ b/apm-agent-plugins/apm-redis-plugin/apm-jedis-3-tests/pom.xml @@ -5,7 +5,7 @@ apm-redis-plugin co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-jedis-3-tests diff --git a/apm-agent-plugins/apm-redis-plugin/apm-jedis-plugin/pom.xml b/apm-agent-plugins/apm-redis-plugin/apm-jedis-plugin/pom.xml index 514478a2d7..0017a84c23 100644 --- a/apm-agent-plugins/apm-redis-plugin/apm-jedis-plugin/pom.xml +++ b/apm-agent-plugins/apm-redis-plugin/apm-jedis-plugin/pom.xml @@ -5,7 +5,7 @@ apm-redis-plugin co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-jedis-plugin diff --git a/apm-agent-plugins/apm-redis-plugin/apm-lettuce-3-tests/pom.xml b/apm-agent-plugins/apm-redis-plugin/apm-lettuce-3-tests/pom.xml index 35222ade43..0490982b65 100644 --- a/apm-agent-plugins/apm-redis-plugin/apm-lettuce-3-tests/pom.xml +++ b/apm-agent-plugins/apm-redis-plugin/apm-lettuce-3-tests/pom.xml @@ -3,7 +3,7 @@ apm-redis-plugin co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/apm-agent-plugins/apm-redis-plugin/apm-lettuce-plugin/pom.xml b/apm-agent-plugins/apm-redis-plugin/apm-lettuce-plugin/pom.xml index e3daf5dd85..fa7860a849 100644 --- a/apm-agent-plugins/apm-redis-plugin/apm-lettuce-plugin/pom.xml +++ b/apm-agent-plugins/apm-redis-plugin/apm-lettuce-plugin/pom.xml @@ -3,7 +3,7 @@ apm-redis-plugin co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/apm-agent-plugins/apm-redis-plugin/apm-redis-common/pom.xml b/apm-agent-plugins/apm-redis-plugin/apm-redis-common/pom.xml index 2bf3ce50e2..8d6c84ac4d 100644 --- a/apm-agent-plugins/apm-redis-plugin/apm-redis-common/pom.xml +++ b/apm-agent-plugins/apm-redis-plugin/apm-redis-common/pom.xml @@ -5,7 +5,7 @@ apm-redis-plugin co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-redis-common diff --git a/apm-agent-plugins/apm-redis-plugin/apm-redisson-plugin/pom.xml b/apm-agent-plugins/apm-redis-plugin/apm-redisson-plugin/pom.xml index 064cb8a48f..4fb2e04d95 100644 --- a/apm-agent-plugins/apm-redis-plugin/apm-redisson-plugin/pom.xml +++ b/apm-agent-plugins/apm-redis-plugin/apm-redisson-plugin/pom.xml @@ -5,7 +5,7 @@ apm-redis-plugin co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-redisson-plugin diff --git a/apm-agent-plugins/apm-redis-plugin/pom.xml b/apm-agent-plugins/apm-redis-plugin/pom.xml index f86ad01f1f..3008ab169d 100644 --- a/apm-agent-plugins/apm-redis-plugin/pom.xml +++ b/apm-agent-plugins/apm-redis-plugin/pom.xml @@ -5,7 +5,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-redis-plugin diff --git a/apm-agent-plugins/apm-scala-concurrent-plugin/pom.xml b/apm-agent-plugins/apm-scala-concurrent-plugin/pom.xml index 7be4890c5f..c251cbfe3b 100644 --- a/apm-agent-plugins/apm-scala-concurrent-plugin/pom.xml +++ b/apm-agent-plugins/apm-scala-concurrent-plugin/pom.xml @@ -5,7 +5,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-scala-concurrent-plugin diff --git a/apm-agent-plugins/apm-scheduled-annotation-plugin-jakartaee-test/pom.xml b/apm-agent-plugins/apm-scheduled-annotation-plugin-jakartaee-test/pom.xml index 362f2727f7..5cceb3b720 100644 --- a/apm-agent-plugins/apm-scheduled-annotation-plugin-jakartaee-test/pom.xml +++ b/apm-agent-plugins/apm-scheduled-annotation-plugin-jakartaee-test/pom.xml @@ -3,7 +3,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/apm-agent-plugins/apm-scheduled-annotation-plugin/pom.xml b/apm-agent-plugins/apm-scheduled-annotation-plugin/pom.xml index 63f9cedfb5..7e866d2678 100644 --- a/apm-agent-plugins/apm-scheduled-annotation-plugin/pom.xml +++ b/apm-agent-plugins/apm-scheduled-annotation-plugin/pom.xml @@ -5,7 +5,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-scheduled-annotation-plugin diff --git a/apm-agent-plugins/apm-servlet-jakarta-test/pom.xml b/apm-agent-plugins/apm-servlet-jakarta-test/pom.xml index 47688ad5fa..9e1376129b 100644 --- a/apm-agent-plugins/apm-servlet-jakarta-test/pom.xml +++ b/apm-agent-plugins/apm-servlet-jakarta-test/pom.xml @@ -5,7 +5,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-servlet-jakarta-test diff --git a/apm-agent-plugins/apm-servlet-plugin/pom.xml b/apm-agent-plugins/apm-servlet-plugin/pom.xml index b20da0ad79..20d1c2462d 100644 --- a/apm-agent-plugins/apm-servlet-plugin/pom.xml +++ b/apm-agent-plugins/apm-servlet-plugin/pom.xml @@ -5,7 +5,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-servlet-plugin diff --git a/apm-agent-plugins/apm-sparkjava-plugin/pom.xml b/apm-agent-plugins/apm-sparkjava-plugin/pom.xml index c5a38e175a..d3e0f46843 100644 --- a/apm-agent-plugins/apm-sparkjava-plugin/pom.xml +++ b/apm-agent-plugins/apm-sparkjava-plugin/pom.xml @@ -3,7 +3,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/apm-agent-plugins/apm-spring-resttemplate/apm-spring-resttemplate-plugin/pom.xml b/apm-agent-plugins/apm-spring-resttemplate/apm-spring-resttemplate-plugin/pom.xml index a649b83cad..509dfa6f99 100644 --- a/apm-agent-plugins/apm-spring-resttemplate/apm-spring-resttemplate-plugin/pom.xml +++ b/apm-agent-plugins/apm-spring-resttemplate/apm-spring-resttemplate-plugin/pom.xml @@ -5,7 +5,7 @@ co.elastic.apm apm-spring-resttemplate - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-spring-resttemplate-plugin diff --git a/apm-agent-plugins/apm-spring-resttemplate/apm-spring-resttemplate-test/pom.xml b/apm-agent-plugins/apm-spring-resttemplate/apm-spring-resttemplate-test/pom.xml index bfe841f571..f08325f563 100644 --- a/apm-agent-plugins/apm-spring-resttemplate/apm-spring-resttemplate-test/pom.xml +++ b/apm-agent-plugins/apm-spring-resttemplate/apm-spring-resttemplate-test/pom.xml @@ -5,7 +5,7 @@ co.elastic.apm apm-spring-resttemplate - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-spring-resttemplate-test diff --git a/apm-agent-plugins/apm-spring-resttemplate/pom.xml b/apm-agent-plugins/apm-spring-resttemplate/pom.xml index c5c29faa49..a1c1551134 100644 --- a/apm-agent-plugins/apm-spring-resttemplate/pom.xml +++ b/apm-agent-plugins/apm-spring-resttemplate/pom.xml @@ -5,7 +5,7 @@ co.elastic.apm apm-agent-plugins - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-spring-resttemplate diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/pom.xml b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/pom.xml index 458e1c1f0d..6ff5380e2f 100644 --- a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/pom.xml +++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-plugin/pom.xml @@ -5,7 +5,7 @@ co.elastic.apm apm-spring-webflux - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-spring-webflux-plugin diff --git a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-testapp/pom.xml b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-testapp/pom.xml index bc43caf827..06389f49ab 100644 --- a/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-testapp/pom.xml +++ b/apm-agent-plugins/apm-spring-webflux/apm-spring-webflux-testapp/pom.xml @@ -5,7 +5,7 @@ co.elastic.apm apm-spring-webflux - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-spring-webflux-testapp diff --git a/apm-agent-plugins/apm-spring-webflux/pom.xml b/apm-agent-plugins/apm-spring-webflux/pom.xml index aa987e32ad..66a54ea05e 100644 --- a/apm-agent-plugins/apm-spring-webflux/pom.xml +++ b/apm-agent-plugins/apm-spring-webflux/pom.xml @@ -5,7 +5,7 @@ co.elastic.apm apm-agent-plugins - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-spring-webflux diff --git a/apm-agent-plugins/apm-spring-webmvc-plugin/pom.xml b/apm-agent-plugins/apm-spring-webmvc-plugin/pom.xml index 3b28a93479..889d713269 100644 --- a/apm-agent-plugins/apm-spring-webmvc-plugin/pom.xml +++ b/apm-agent-plugins/apm-spring-webmvc-plugin/pom.xml @@ -5,7 +5,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-spring-webmvc-plugin diff --git a/apm-agent-plugins/apm-struts-plugin/pom.xml b/apm-agent-plugins/apm-struts-plugin/pom.xml index bbb7aa79ed..b3526c57dc 100644 --- a/apm-agent-plugins/apm-struts-plugin/pom.xml +++ b/apm-agent-plugins/apm-struts-plugin/pom.xml @@ -3,7 +3,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/apm-agent-plugins/apm-urlconnection-plugin/pom.xml b/apm-agent-plugins/apm-urlconnection-plugin/pom.xml index f556acddbe..1d9ba17efa 100644 --- a/apm-agent-plugins/apm-urlconnection-plugin/pom.xml +++ b/apm-agent-plugins/apm-urlconnection-plugin/pom.xml @@ -5,7 +5,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-urlconnection-plugin diff --git a/apm-agent-plugins/apm-vertx/apm-vertx-common/pom.xml b/apm-agent-plugins/apm-vertx/apm-vertx-common/pom.xml index 6fdf93ba0b..eb6b3a9c26 100644 --- a/apm-agent-plugins/apm-vertx/apm-vertx-common/pom.xml +++ b/apm-agent-plugins/apm-vertx/apm-vertx-common/pom.xml @@ -5,7 +5,7 @@ apm-vertx co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-vertx-common diff --git a/apm-agent-plugins/apm-vertx/apm-vertx3-plugin/pom.xml b/apm-agent-plugins/apm-vertx/apm-vertx3-plugin/pom.xml index c4ea5c2f03..9d3136dbaf 100644 --- a/apm-agent-plugins/apm-vertx/apm-vertx3-plugin/pom.xml +++ b/apm-agent-plugins/apm-vertx/apm-vertx3-plugin/pom.xml @@ -5,7 +5,7 @@ apm-vertx co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-vertx3-plugin diff --git a/apm-agent-plugins/apm-vertx/apm-vertx3-test-latest/pom.xml b/apm-agent-plugins/apm-vertx/apm-vertx3-test-latest/pom.xml index 536f914878..ecb5af3524 100644 --- a/apm-agent-plugins/apm-vertx/apm-vertx3-test-latest/pom.xml +++ b/apm-agent-plugins/apm-vertx/apm-vertx3-test-latest/pom.xml @@ -5,7 +5,7 @@ apm-vertx co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-vertx3-test-latest diff --git a/apm-agent-plugins/apm-vertx/apm-vertx4-plugin/pom.xml b/apm-agent-plugins/apm-vertx/apm-vertx4-plugin/pom.xml index b7569f7647..b4e17733d6 100644 --- a/apm-agent-plugins/apm-vertx/apm-vertx4-plugin/pom.xml +++ b/apm-agent-plugins/apm-vertx/apm-vertx4-plugin/pom.xml @@ -5,7 +5,7 @@ apm-vertx co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-vertx4-plugin diff --git a/apm-agent-plugins/apm-vertx/pom.xml b/apm-agent-plugins/apm-vertx/pom.xml index 4b21068235..85ffe7f37f 100644 --- a/apm-agent-plugins/apm-vertx/pom.xml +++ b/apm-agent-plugins/apm-vertx/pom.xml @@ -5,7 +5,7 @@ apm-agent-plugins co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-vertx diff --git a/apm-agent-plugins/pom.xml b/apm-agent-plugins/pom.xml index afe3885539..bd3862cac1 100644 --- a/apm-agent-plugins/pom.xml +++ b/apm-agent-plugins/pom.xml @@ -5,7 +5,7 @@ apm-agent-parent co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-agent-plugins @@ -58,6 +58,7 @@ apm-jdk-httpserver-plugin apm-rabbitmq apm-okhttp-test + apm-opentelemetry apm-cassandra apm-struts-plugin apm-vertx diff --git a/apm-agent/pom.xml b/apm-agent/pom.xml index 62a3234078..ea01a01ea6 100644 --- a/apm-agent/pom.xml +++ b/apm-agent/pom.xml @@ -5,7 +5,7 @@ co.elastic.apm apm-agent-parent - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-agent @@ -181,6 +181,11 @@ apm-log4j2-plugin ${project.version} + + ${project.groupId} + apm-opentelemetry-plugin + ${project.version} + ${project.groupId} apm-logback-plugin-impl diff --git a/apm-opentracing/pom.xml b/apm-opentracing/pom.xml index 82e3eaf3ac..693dd35462 100644 --- a/apm-opentracing/pom.xml +++ b/apm-opentracing/pom.xml @@ -5,7 +5,7 @@ apm-agent-parent co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT apm-opentracing diff --git a/docs/public-api.asciidoc b/docs/api-elastic.asciidoc similarity index 99% rename from docs/public-api.asciidoc rename to docs/api-elastic.asciidoc index 40ad07cefc..65297186fc 100644 --- a/docs/public-api.asciidoc +++ b/docs/api-elastic.asciidoc @@ -5,7 +5,7 @@ please view this documentation at https://www.elastic.co/guide/en/apm/agent/java endif::[] [[public-api]] -== Public API +=== Public API The public API of the Elastic APM Java agent lets you customize and manually create spans and transactions, as well as track errors. @@ -803,7 +803,7 @@ Using `null` or empty resource string will result in the omission of this field [float] [[api-span-set-destination-address]] ==== `Span setDestinationAddress(String address, int port)` added[1.25.0] -Provides a way to manually set the span's `destination.address` and `destination.port` fields. +Provides a way to manually set the span's `destination.address` and `destination.port` fields. Values set through this method will take precedence over the automatically discovered ones. Using `null` or empty address or non-positive port will result in the omission of the corresponding field from the span context. diff --git a/docs/api-opentelemetry.asciidoc b/docs/api-opentelemetry.asciidoc new file mode 100644 index 0000000000..2572a9fc24 --- /dev/null +++ b/docs/api-opentelemetry.asciidoc @@ -0,0 +1,146 @@ +ifdef::env-github[] +NOTE: For the best reading experience, +please view this documentation at https://www.elastic.co/guide/en/apm/agent/java[elastic.co] +endif::[] + +[[opentelementry-bridge]] +=== OpenTelemetry bridge + +NOTE: Added as experimental in 1.30.0. +To enable it, set <> to `true`. + +The Elastic APM OpenTelemetry bridge allows creating Elastic APM `Transactions` and `Spans`, +using the OpenTelemetry API. +In other words, +it translates the calls to the OpenTelemetry API to Elastic APM and thus allows for reusing existing instrumentation. + +NOTE: While manual instrumentations using the OpenTelemetry API can be adapted to the Elastic APM Java agent, it's not possible to use the instrumentations from +https://github.com/open-telemetry/opentelemetry-java-instrumentation[opentelemetry-java-instrumentation] in the context of the Elastic APM Java agent. + +However, you can use https://github.com/open-telemetry/opentelemetry-java-instrumentation[opentelemetry-java-instrumentation] (aka the OpenTelementry Java agent) +and send the data to APM Server. +See the {apm-guide-ref}/open-telemetry.html[OpenTelemetry integration docs] for more details. + +The first span of a service will be converted to an Elastic APM +{apm-guide-ref}/data-model-transactions.html[`Transaction`], +subsequent spans are mapped to Elastic APM +{apm-guide-ref}/data-model-spans.html[`Span`]. + +[float] +[[otel-getting-started]] +==== Getting started +The first step in getting started with the OpenTelemetry API bridge is to declare a dependency to the API: + +[source,xml] +.pom.xml +---- + + io.opentelemetry + opentelemetry-api + ${version.opentelemetry} + +---- + +[source,groovy] +.build.gradle +---- +compile "io.opentelemetry:opentelemetry-api:$openTelemetryVersion" +---- + +The minimum required OpenTelemetry version is 1.0.1. + +[float] +[[otel-init-tracer]] +==== Initialize tracer + +There's no separate dependency needed for the bridge itself. +The Java agent hooks into `GlobalOpenTelemetry` to return it's own implementation of `OpenTelemetry` +that is connected to the internal tracer of the agent. + +[source,java] +---- +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.trace.Tracer; + +OpenTelemetry openTelemetry = GlobalOpenTelemetry.get(); +Tracer tracer = openTelemetry.getTracer(""); + +---- + +To disable that behavior, +and to rely on the standard discovery mechanism of `GlobalOpenTelemetry`, +you can set <> to `opentelemetry`. + +[float] +[[otel-set-attribute]] +==== Add custom metadata to a span + +If you like the spans created by the Elastic APM Java agent's auto-instrumentation, +but you want to add a custom label, +you can use the OpenTelemetry API to get ahold of the current span and call `setAttribute`: + +[source,java] +---- +Span.current().setAttribute("foo", "bar"); +---- + +[float] +[[otel-create-transaction-span]] +==== Create a child of the active span + +This is an example for adding a custom span to the span created by the Java agent's auto-instrumentation. + +[source,java] +---- +// if there's an active span, it will implicitly be the parent +// in case there's no parent, the custom span will become a Elastic APM transaction +Span custom = tracer.spanBuilder("my custom span").startSpan(); +// making your child the current one makes the Java agent aware of this span +// if the agent creates spans in the context of myTracedMethod() (such as outgoing requests), +// they'll be added as a child of your custom span +try (Scope scope = custom.makeCurrent()) { + myTracedMethod(); +} catch (Exception e) { + custom.recordException(e); + throw e; +} finally { + custom.end(); +} +---- + +To learn more about the OpenTelemetry API, +head over do https://opentelemetry.io/docs/java/manual_instrumentation/[their documentation]. + +[float] +[[otel-caveats]] +==== Caveats +Not all features of the OpenTelemetry API are supported. + +[float] +[[otel-metrics]] +===== Metrics +This bridge only supports the tracing API. +The Metrics API is currently not supported. + +[float] +[[otel-propagation]] +===== In process context propagation +Entries that are added to the current context, +`Context.current().with(...).makeCurrent()` cannot be retrieved via `Context.current().get(...)`. + +[float] +[[otel-references]] +===== Span References +The `SpanBuilder#addLink` method is currently not supported. +Spans can only have a single parent (`SpanBuilder#setParent`) + +[float] +[[otel-baggage]] +===== Baggage +Propagating baggage within or outside the process is not supported. +Baggage items are silently dropped. + +[float] +[[otel-events]] +===== Events +Events are silently dropped, for example `Span.current().addEvent("my event")`. diff --git a/docs/opentracing.asciidoc b/docs/api-opentracing.asciidoc similarity index 74% rename from docs/opentracing.asciidoc rename to docs/api-opentracing.asciidoc index 94c056783b..1a30828e76 100644 --- a/docs/opentracing.asciidoc +++ b/docs/api-opentracing.asciidoc @@ -4,11 +4,12 @@ please view this documentation at https://www.elastic.co/guide/en/apm/agent/java endif::[] [[opentracing-bridge]] -== Elastic APM OpenTracing bridge +=== OpenTracing bridge -NOTE: Latest supported OpenTracing version: 0.33 (as of agent and OpenTracing-bridge version 1.9.0) +NOTE: OpenTracing is discontinued in favor of OpenTelemetry. Consider using the <> instead. + +Latest supported OpenTracing version: 0.33 (as of agent and OpenTracing-bridge version 1.9.0) -The Elastic APM OpenTracing bridge allows to create Elastic APM `Transactions` and `Spans`, +The Elastic APM OpenTracing bridge allows creating Elastic APM `Transactions` and `Spans`, using the OpenTracing API. In other words, it translates the calls to the OpenTracing API to Elastic APM and thus allows for reusing existing instrumentation. @@ -18,38 +19,10 @@ The first span of a service will be converted to an Elastic APM subsequent spans are mapped to Elastic APM {apm-guide-ref}/data-model-spans.html[`Span`]. -[float] -[[operation-modes]] -=== Operation Modes - -This bridge allows for different operation modes in combination with the Elastic APM `javaagent` - -Noop:: -+ --- -If the `javaagent` is not specified, the bridge is in noop mode and does not actually record and report spans. --- - -Mix and Match:: -+ --- -If you want to leverage the auto instrumentation of Elastic APM, -but also want do create custom spans or use the OpenTracing API to add custom tags to the spans created by Elastic APM, -you can just do that. -The OpenTracing bridge and the standard Elastic APM API interact seamlessly. --- - -Manual instrumentation:: -+ --- -If you don't want Elastic APM to auto-instrument known frameworks, -but instead only rely on manual instrumentation, -disable the auto instrumentation setting the configuration option <> to `false`. --- [float] -[[getting-started]] -=== Getting started +[[opentracing-getting-started]] +==== Getting started The first step in getting started with the OpenTracing API bridge is to declare a dependency to the API: [source,xml] @@ -76,7 +49,7 @@ image:https://img.shields.io/maven-central/v/co.elastic.apm/apm-opentracing.svg[ [float] [[init-tracer]] -=== Initialize tracer +==== Initialize tracer [source,java] ---- @@ -94,8 +67,8 @@ When such is used, no code changes are required, only the addition of dependenci the Elastic OpenTracing bridge. [float] -[[elastic-apm-tags]] -=== Elastic APM specific tags +[[opentracing-elastic-apm-tags]] +==== Elastic APM specific tags Elastic APM defines some tags which are not included in the OpenTracing API but are relevant in the context of Elastic APM. @@ -116,32 +89,32 @@ Elastic APM defines some tags which are not included in the OpenTracing API but Setting `http.status_code` to `200`, for example, implicitly sets the result to `HTTP 2xx` if not explicitly set otherwise. [float] -[[unsupported]] -=== Caveats +[[opentracing-unsupported]] +==== Caveats Not all features of the OpenTracing API are supported. [float] -[[propagation]] -==== Context propagation +[[opentracing-propagation]] +===== Context propagation This bridge only supports the formats `Format.Builtin.TEXT_MAP` and `Format.Builtin.HTTP_HEADERS`. `Format.Builtin.BINARY` is currently not supported. [float] -[[references]] -==== Span References +[[opentracing-references]] +===== Span References Currently, this bridge only supports `child_of` references. Other references, like `follows_from` are not supported yet. [float] -[[baggage]] -==== Baggage +[[opentracing-baggage]] +===== Baggage The `Span.setBaggageItem(String, String)` method is not supported. Baggage items are silently dropped. [float] -[[logs]] -==== Logs +[[opentracing-logs]] +===== Logs Only exception logging is supported. Logging an Exception on the OpenTracing span will create an Elastic APM {apm-guide-ref}/data-model-errors.html[`Error`]. diff --git a/docs/apis.asciidoc b/docs/apis.asciidoc new file mode 100644 index 0000000000..f47febf6f0 --- /dev/null +++ b/docs/apis.asciidoc @@ -0,0 +1,50 @@ +ifdef::env-github[] +NOTE: For the best reading experience, +please view this documentation at https://www.elastic.co/guide/en/apm/agent/java[elastic.co] +endif::[] + +[[apis]] +== Tracing APIs + +There are three different ways enhance the out-of-the-box instrumentation of the Java agent with manual instrumentation: + +. <> + + A simple and stable API that is most native to the agent. + Contains annotations to declaratively create spans. +. <> + + A vendor neutral API. + If you plan to do a lot of manual instrumentation and want to reduce vendor lock-in this is probably what you're looking for. +. <> + + A vendor neutral API that is discontinued in favor of OpenTelemetry. + +[float] +[[apis-operation-modes]] +== Operation Modes + +All APIs allow for different operation modes in combination with the Elastic APM agent + +Noop:: ++ +-- +If the agent is not installed, the APIs are in noop mode and do not actually record and report spans. +-- + +Mix and Match:: ++ +-- +If you want to leverage the auto instrumentation of Elastic APM, +but also want to create custom spans or use the API to add custom labels to the spans created by Elastic APM, +you can just do that. +-- + +Manual instrumentation:: ++ +-- +If you don't want Elastic APM to auto-instrument known frameworks, +but instead only rely on manual instrumentation, +disable the auto instrumentation setting the configuration option <> to `false`. +-- + +include::./api-elastic.asciidoc[Public API] +include::./api-opentelemetry.asciidoc[OpenTelemetry API Bridge] +include::./api-opentracing.asciidoc[OpenTracing API Bridge] diff --git a/docs/configuration.asciidoc b/docs/configuration.asciidoc index c6a8e547ef..b1d6c382ea 100644 --- a/docs/configuration.asciidoc +++ b/docs/configuration.asciidoc @@ -761,7 +761,7 @@ you should add an additional entry to this list (make sure to also include the d ==== `enable_instrumentations` (added[1.28.0]) A list of instrumentations which should be selectively enabled. -Valid options are `annotations`, `annotations-capture-span`, `annotations-capture-transaction`, `annotations-traced`, `apache-commons-exec`, `apache-httpclient`, `asynchttpclient`, `aws-lambda`, `cassandra`, `concurrent`, `dubbo`, `elasticsearch-restclient`, `exception-handler`, `executor`, `executor-collection`, `experimental`, `fork-join`, `grails`, `grpc`, `hibernate-search`, `http-client`, `jakarta-websocket`, `javalin`, `javax-websocket`, `jax-rs`, `jax-ws`, `jboss-logging-correlation`, `jdbc`, `jdk-httpclient`, `jdk-httpserver`, `jedis`, `jms`, `jsf`, `kafka`, `lettuce`, `log4j1-correlation`, `log4j1-ecs`, `log4j1-error`, `log4j2-correlation`, `log4j2-ecs`, `log4j2-error`, `logback-correlation`, `logback-ecs`, `logging`, `micrometer`, `mongodb-client`, `okhttp`, `opentracing`, `process`, `public-api`, `quartz`, `rabbitmq`, `reactor`, `redis`, `redisson`, `render`, `scala-future`, `scheduled`, `servlet-api`, `servlet-api-async`, `servlet-api-dispatch`, `servlet-input-stream`, `slf4j-error`, `sparkjava`, `spring-amqp`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `spring-view-render`, `spring-webflux`, `ssl-context`, `struts`, `timer-task`, `urlconnection`, `vertx`, `vertx-web`, `vertx-webclient`, `websocket`. +Valid options are `annotations`, `annotations-capture-span`, `annotations-capture-transaction`, `annotations-traced`, `apache-commons-exec`, `apache-httpclient`, `asynchttpclient`, `aws-lambda`, `cassandra`, `concurrent`, `dubbo`, `elasticsearch-restclient`, `exception-handler`, `executor`, `executor-collection`, `experimental`, `fork-join`, `grails`, `grpc`, `hibernate-search`, `http-client`, `jakarta-websocket`, `javalin`, `javax-websocket`, `jax-rs`, `jax-ws`, `jboss-logging-correlation`, `jdbc`, `jdk-httpclient`, `jdk-httpserver`, `jedis`, `jms`, `jsf`, `kafka`, `lettuce`, `log4j1-correlation`, `log4j1-ecs`, `log4j1-error`, `log4j2-correlation`, `log4j2-ecs`, `log4j2-error`, `logback-correlation`, `logback-ecs`, `logging`, `micrometer`, `mongodb-client`, `okhttp`, `opentelemetry`, `opentracing`, `process`, `public-api`, `quartz`, `rabbitmq`, `reactor`, `redis`, `redisson`, `render`, `scala-future`, `scheduled`, `servlet-api`, `servlet-api-async`, `servlet-api-dispatch`, `servlet-input-stream`, `slf4j-error`, `sparkjava`, `spring-amqp`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `spring-view-render`, `spring-webflux`, `ssl-context`, `struts`, `timer-task`, `urlconnection`, `vertx`, `vertx-web`, `vertx-webclient`, `websocket`. When set to non-empty value, only listed instrumentations will be enabled if they are not disabled through <> or <>. When not set or empty (default), all instrumentations enabled by default will be enabled unless they are disabled through <> or <>. @@ -789,7 +789,7 @@ NOTE: Changing this value at runtime can slow down the application temporarily. ==== `disable_instrumentations` (added[1.0.0,Changing this value at runtime is possible since version 1.15.0]) A list of instrumentations which should be disabled. -Valid options are `annotations`, `annotations-capture-span`, `annotations-capture-transaction`, `annotations-traced`, `apache-commons-exec`, `apache-httpclient`, `asynchttpclient`, `aws-lambda`, `cassandra`, `concurrent`, `dubbo`, `elasticsearch-restclient`, `exception-handler`, `executor`, `executor-collection`, `experimental`, `fork-join`, `grails`, `grpc`, `hibernate-search`, `http-client`, `jakarta-websocket`, `javalin`, `javax-websocket`, `jax-rs`, `jax-ws`, `jboss-logging-correlation`, `jdbc`, `jdk-httpclient`, `jdk-httpserver`, `jedis`, `jms`, `jsf`, `kafka`, `lettuce`, `log4j1-correlation`, `log4j1-ecs`, `log4j1-error`, `log4j2-correlation`, `log4j2-ecs`, `log4j2-error`, `logback-correlation`, `logback-ecs`, `logging`, `micrometer`, `mongodb-client`, `okhttp`, `opentracing`, `process`, `public-api`, `quartz`, `rabbitmq`, `reactor`, `redis`, `redisson`, `render`, `scala-future`, `scheduled`, `servlet-api`, `servlet-api-async`, `servlet-api-dispatch`, `servlet-input-stream`, `slf4j-error`, `sparkjava`, `spring-amqp`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `spring-view-render`, `spring-webflux`, `ssl-context`, `struts`, `timer-task`, `urlconnection`, `vertx`, `vertx-web`, `vertx-webclient`, `websocket`. +Valid options are `annotations`, `annotations-capture-span`, `annotations-capture-transaction`, `annotations-traced`, `apache-commons-exec`, `apache-httpclient`, `asynchttpclient`, `aws-lambda`, `cassandra`, `concurrent`, `dubbo`, `elasticsearch-restclient`, `exception-handler`, `executor`, `executor-collection`, `experimental`, `fork-join`, `grails`, `grpc`, `hibernate-search`, `http-client`, `jakarta-websocket`, `javalin`, `javax-websocket`, `jax-rs`, `jax-ws`, `jboss-logging-correlation`, `jdbc`, `jdk-httpclient`, `jdk-httpserver`, `jedis`, `jms`, `jsf`, `kafka`, `lettuce`, `log4j1-correlation`, `log4j1-ecs`, `log4j1-error`, `log4j2-correlation`, `log4j2-ecs`, `log4j2-error`, `logback-correlation`, `logback-ecs`, `logging`, `micrometer`, `mongodb-client`, `okhttp`, `opentelemetry`, `opentracing`, `process`, `public-api`, `quartz`, `rabbitmq`, `reactor`, `redis`, `redisson`, `render`, `scala-future`, `scheduled`, `servlet-api`, `servlet-api-async`, `servlet-api-dispatch`, `servlet-input-stream`, `slf4j-error`, `sparkjava`, `spring-amqp`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `spring-view-render`, `spring-webflux`, `ssl-context`, `struts`, `timer-task`, `urlconnection`, `vertx`, `vertx-web`, `vertx-webclient`, `websocket`. For version `1.25.0` and later, use <> to enable experimental instrumentations. NOTE: Changing this value at runtime can slow down the application temporarily. @@ -3147,7 +3147,7 @@ Example: `5ms`. # sanitize_field_names=password,passwd,pwd,secret,*key,*token*,*session*,*credit*,*card*,*auth*,set-cookie # A list of instrumentations which should be selectively enabled. -# Valid options are `annotations`, `annotations-capture-span`, `annotations-capture-transaction`, `annotations-traced`, `apache-commons-exec`, `apache-httpclient`, `asynchttpclient`, `aws-lambda`, `cassandra`, `concurrent`, `dubbo`, `elasticsearch-restclient`, `exception-handler`, `executor`, `executor-collection`, `experimental`, `fork-join`, `grails`, `grpc`, `hibernate-search`, `http-client`, `jakarta-websocket`, `javalin`, `javax-websocket`, `jax-rs`, `jax-ws`, `jboss-logging-correlation`, `jdbc`, `jdk-httpclient`, `jdk-httpserver`, `jedis`, `jms`, `jsf`, `kafka`, `lettuce`, `log4j1-correlation`, `log4j1-ecs`, `log4j1-error`, `log4j2-correlation`, `log4j2-ecs`, `log4j2-error`, `logback-correlation`, `logback-ecs`, `logging`, `micrometer`, `mongodb-client`, `okhttp`, `opentracing`, `process`, `public-api`, `quartz`, `rabbitmq`, `reactor`, `redis`, `redisson`, `render`, `scala-future`, `scheduled`, `servlet-api`, `servlet-api-async`, `servlet-api-dispatch`, `servlet-input-stream`, `slf4j-error`, `sparkjava`, `spring-amqp`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `spring-view-render`, `spring-webflux`, `ssl-context`, `struts`, `timer-task`, `urlconnection`, `vertx`, `vertx-web`, `vertx-webclient`, `websocket`. +# Valid options are `annotations`, `annotations-capture-span`, `annotations-capture-transaction`, `annotations-traced`, `apache-commons-exec`, `apache-httpclient`, `asynchttpclient`, `aws-lambda`, `cassandra`, `concurrent`, `dubbo`, `elasticsearch-restclient`, `exception-handler`, `executor`, `executor-collection`, `experimental`, `fork-join`, `grails`, `grpc`, `hibernate-search`, `http-client`, `jakarta-websocket`, `javalin`, `javax-websocket`, `jax-rs`, `jax-ws`, `jboss-logging-correlation`, `jdbc`, `jdk-httpclient`, `jdk-httpserver`, `jedis`, `jms`, `jsf`, `kafka`, `lettuce`, `log4j1-correlation`, `log4j1-ecs`, `log4j1-error`, `log4j2-correlation`, `log4j2-ecs`, `log4j2-error`, `logback-correlation`, `logback-ecs`, `logging`, `micrometer`, `mongodb-client`, `okhttp`, `opentelemetry`, `opentracing`, `process`, `public-api`, `quartz`, `rabbitmq`, `reactor`, `redis`, `redisson`, `render`, `scala-future`, `scheduled`, `servlet-api`, `servlet-api-async`, `servlet-api-dispatch`, `servlet-input-stream`, `slf4j-error`, `sparkjava`, `spring-amqp`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `spring-view-render`, `spring-webflux`, `ssl-context`, `struts`, `timer-task`, `urlconnection`, `vertx`, `vertx-web`, `vertx-webclient`, `websocket`. # When set to non-empty value, only listed instrumentations will be enabled if they are not disabled through <> or <>. # When not set or empty (default), all instrumentations enabled by default will be enabled unless they are disabled through <> or <>. # @@ -3160,7 +3160,7 @@ Example: `5ms`. # enable_instrumentations= # A list of instrumentations which should be disabled. -# Valid options are `annotations`, `annotations-capture-span`, `annotations-capture-transaction`, `annotations-traced`, `apache-commons-exec`, `apache-httpclient`, `asynchttpclient`, `aws-lambda`, `cassandra`, `concurrent`, `dubbo`, `elasticsearch-restclient`, `exception-handler`, `executor`, `executor-collection`, `experimental`, `fork-join`, `grails`, `grpc`, `hibernate-search`, `http-client`, `jakarta-websocket`, `javalin`, `javax-websocket`, `jax-rs`, `jax-ws`, `jboss-logging-correlation`, `jdbc`, `jdk-httpclient`, `jdk-httpserver`, `jedis`, `jms`, `jsf`, `kafka`, `lettuce`, `log4j1-correlation`, `log4j1-ecs`, `log4j1-error`, `log4j2-correlation`, `log4j2-ecs`, `log4j2-error`, `logback-correlation`, `logback-ecs`, `logging`, `micrometer`, `mongodb-client`, `okhttp`, `opentracing`, `process`, `public-api`, `quartz`, `rabbitmq`, `reactor`, `redis`, `redisson`, `render`, `scala-future`, `scheduled`, `servlet-api`, `servlet-api-async`, `servlet-api-dispatch`, `servlet-input-stream`, `slf4j-error`, `sparkjava`, `spring-amqp`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `spring-view-render`, `spring-webflux`, `ssl-context`, `struts`, `timer-task`, `urlconnection`, `vertx`, `vertx-web`, `vertx-webclient`, `websocket`. +# Valid options are `annotations`, `annotations-capture-span`, `annotations-capture-transaction`, `annotations-traced`, `apache-commons-exec`, `apache-httpclient`, `asynchttpclient`, `aws-lambda`, `cassandra`, `concurrent`, `dubbo`, `elasticsearch-restclient`, `exception-handler`, `executor`, `executor-collection`, `experimental`, `fork-join`, `grails`, `grpc`, `hibernate-search`, `http-client`, `jakarta-websocket`, `javalin`, `javax-websocket`, `jax-rs`, `jax-ws`, `jboss-logging-correlation`, `jdbc`, `jdk-httpclient`, `jdk-httpserver`, `jedis`, `jms`, `jsf`, `kafka`, `lettuce`, `log4j1-correlation`, `log4j1-ecs`, `log4j1-error`, `log4j2-correlation`, `log4j2-ecs`, `log4j2-error`, `logback-correlation`, `logback-ecs`, `logging`, `micrometer`, `mongodb-client`, `okhttp`, `opentelemetry`, `opentracing`, `process`, `public-api`, `quartz`, `rabbitmq`, `reactor`, `redis`, `redisson`, `render`, `scala-future`, `scheduled`, `servlet-api`, `servlet-api-async`, `servlet-api-dispatch`, `servlet-input-stream`, `slf4j-error`, `sparkjava`, `spring-amqp`, `spring-mvc`, `spring-resttemplate`, `spring-service-name`, `spring-view-render`, `spring-webflux`, `ssl-context`, `struts`, `timer-task`, `urlconnection`, `vertx`, `vertx-web`, `vertx-webclient`, `websocket`. # For version `1.25.0` and later, use <> to enable experimental instrumentations. # # NOTE: Changing this value at runtime can slow down the application temporarily. diff --git a/docs/index.asciidoc b/docs/index.asciidoc index cfc996129f..25a2bf9252 100644 --- a/docs/index.asciidoc +++ b/docs/index.asciidoc @@ -16,9 +16,8 @@ include::./intro.asciidoc[Introduction] include::./setup.asciidoc[Set up the agent] include::./supported-technologies.asciidoc[Supported Technologies] include::./configuration.asciidoc[Configuration] -include::./public-api.asciidoc[API documentation] +include::./apis.asciidoc[Programmatic APIs] include::./metrics.asciidoc[Metrics] -include::./opentracing.asciidoc[OpenTracing API documentation] include::./log-correlation.asciidoc[Log correlation] include::./method-monitoring.asciidoc[Java method monitoring] include::./tuning-and-overhead.asciidoc[Tuning and Overhead considerations] diff --git a/elastic-apm-agent/pom.xml b/elastic-apm-agent/pom.xml index 3586949dc9..6cb5511bef 100644 --- a/elastic-apm-agent/pom.xml +++ b/elastic-apm-agent/pom.xml @@ -5,7 +5,7 @@ co.elastic.apm apm-agent-parent - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT elastic-apm-agent diff --git a/integration-tests/application-server-integration-tests/pom.xml b/integration-tests/application-server-integration-tests/pom.xml index 7c5a4a182c..06916c2b20 100644 --- a/integration-tests/application-server-integration-tests/pom.xml +++ b/integration-tests/application-server-integration-tests/pom.xml @@ -5,7 +5,7 @@ integration-tests co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT application-server-integration-tests diff --git a/integration-tests/cdi-app/cdi-app-dependent/pom.xml b/integration-tests/cdi-app/cdi-app-dependent/pom.xml index 09e5034a89..46ec11d619 100644 --- a/integration-tests/cdi-app/cdi-app-dependent/pom.xml +++ b/integration-tests/cdi-app/cdi-app-dependent/pom.xml @@ -4,7 +4,7 @@ cdi-app co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/integration-tests/cdi-app/cdi-app-standalone/pom.xml b/integration-tests/cdi-app/cdi-app-standalone/pom.xml index a28ada37ca..8fb27b564f 100644 --- a/integration-tests/cdi-app/cdi-app-standalone/pom.xml +++ b/integration-tests/cdi-app/cdi-app-standalone/pom.xml @@ -4,7 +4,7 @@ cdi-app co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/integration-tests/cdi-app/pom.xml b/integration-tests/cdi-app/pom.xml index 18eda57159..d1914eb51f 100644 --- a/integration-tests/cdi-app/pom.xml +++ b/integration-tests/cdi-app/pom.xml @@ -4,7 +4,7 @@ integration-tests co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/integration-tests/cdi-jakartaee-app/cdi-jakartaee-app-dependent/pom.xml b/integration-tests/cdi-jakartaee-app/cdi-jakartaee-app-dependent/pom.xml index 3061a761ef..80c7429c3d 100644 --- a/integration-tests/cdi-jakartaee-app/cdi-jakartaee-app-dependent/pom.xml +++ b/integration-tests/cdi-jakartaee-app/cdi-jakartaee-app-dependent/pom.xml @@ -4,7 +4,7 @@ cdi-jakartaee-app co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/integration-tests/cdi-jakartaee-app/cdi-jakartaee-app-standalone/pom.xml b/integration-tests/cdi-jakartaee-app/cdi-jakartaee-app-standalone/pom.xml index cc064d6419..7219727dbc 100644 --- a/integration-tests/cdi-jakartaee-app/cdi-jakartaee-app-standalone/pom.xml +++ b/integration-tests/cdi-jakartaee-app/cdi-jakartaee-app-standalone/pom.xml @@ -4,7 +4,7 @@ cdi-jakartaee-app co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/integration-tests/cdi-jakartaee-app/pom.xml b/integration-tests/cdi-jakartaee-app/pom.xml index da721096a7..5653719b89 100644 --- a/integration-tests/cdi-jakartaee-app/pom.xml +++ b/integration-tests/cdi-jakartaee-app/pom.xml @@ -4,7 +4,7 @@ integration-tests co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/integration-tests/external-plugin-test/external-plugin-app/pom.xml b/integration-tests/external-plugin-test/external-plugin-app/pom.xml index 81d617adf0..52cf3b7d50 100644 --- a/integration-tests/external-plugin-test/external-plugin-app/pom.xml +++ b/integration-tests/external-plugin-test/external-plugin-app/pom.xml @@ -6,7 +6,7 @@ external-plugin-test co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT external-plugin-app diff --git a/integration-tests/external-plugin-test/external-plugin-jakarta-app/pom.xml b/integration-tests/external-plugin-test/external-plugin-jakarta-app/pom.xml index a00b6e3986..308aa390ad 100644 --- a/integration-tests/external-plugin-test/external-plugin-jakarta-app/pom.xml +++ b/integration-tests/external-plugin-test/external-plugin-jakarta-app/pom.xml @@ -6,7 +6,7 @@ external-plugin-test co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT external-plugin-jakarta-app diff --git a/integration-tests/external-plugin-test/external-plugin/pom.xml b/integration-tests/external-plugin-test/external-plugin/pom.xml index 3d20fb8ee1..480b376af0 100644 --- a/integration-tests/external-plugin-test/external-plugin/pom.xml +++ b/integration-tests/external-plugin-test/external-plugin/pom.xml @@ -6,7 +6,7 @@ external-plugin-test co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT external-plugin diff --git a/integration-tests/external-plugin-test/plugin-instrumentation-target/pom.xml b/integration-tests/external-plugin-test/plugin-instrumentation-target/pom.xml index 20061d40e4..d0ed4866a1 100644 --- a/integration-tests/external-plugin-test/plugin-instrumentation-target/pom.xml +++ b/integration-tests/external-plugin-test/plugin-instrumentation-target/pom.xml @@ -6,7 +6,7 @@ external-plugin-test co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT plugin-instrumentation-target diff --git a/integration-tests/external-plugin-test/pom.xml b/integration-tests/external-plugin-test/pom.xml index 2dc0f86b74..cc7ee3c593 100644 --- a/integration-tests/external-plugin-test/pom.xml +++ b/integration-tests/external-plugin-test/pom.xml @@ -3,7 +3,7 @@ integration-tests co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/integration-tests/jakartaee-jsf-app/jakartaee-jsf-app-dependent/pom.xml b/integration-tests/jakartaee-jsf-app/jakartaee-jsf-app-dependent/pom.xml index 33a1fe6027..8fd28a5c1a 100644 --- a/integration-tests/jakartaee-jsf-app/jakartaee-jsf-app-dependent/pom.xml +++ b/integration-tests/jakartaee-jsf-app/jakartaee-jsf-app-dependent/pom.xml @@ -3,7 +3,7 @@ jakartaee-jsf-app co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/integration-tests/jakartaee-jsf-app/jakartaee-jsf-app-standalone/pom.xml b/integration-tests/jakartaee-jsf-app/jakartaee-jsf-app-standalone/pom.xml index 9fc9d70373..f24baacbf6 100644 --- a/integration-tests/jakartaee-jsf-app/jakartaee-jsf-app-standalone/pom.xml +++ b/integration-tests/jakartaee-jsf-app/jakartaee-jsf-app-standalone/pom.xml @@ -3,7 +3,7 @@ jakartaee-jsf-app co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/integration-tests/jakartaee-jsf-app/pom.xml b/integration-tests/jakartaee-jsf-app/pom.xml index 049026ee93..66619281fa 100644 --- a/integration-tests/jakartaee-jsf-app/pom.xml +++ b/integration-tests/jakartaee-jsf-app/pom.xml @@ -3,7 +3,7 @@ integration-tests co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 pom diff --git a/integration-tests/jakartaee-simple-webapp/pom.xml b/integration-tests/jakartaee-simple-webapp/pom.xml index e9c3371bd3..af805136e3 100644 --- a/integration-tests/jakartaee-simple-webapp/pom.xml +++ b/integration-tests/jakartaee-simple-webapp/pom.xml @@ -5,7 +5,7 @@ integration-tests co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT jakartaee-simple-webapp diff --git a/integration-tests/jsf-app/jsf-app-dependent/pom.xml b/integration-tests/jsf-app/jsf-app-dependent/pom.xml index 2759940745..c2d5720fcd 100644 --- a/integration-tests/jsf-app/jsf-app-dependent/pom.xml +++ b/integration-tests/jsf-app/jsf-app-dependent/pom.xml @@ -4,7 +4,7 @@ jsf-app co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/integration-tests/jsf-app/jsf-app-standalone/pom.xml b/integration-tests/jsf-app/jsf-app-standalone/pom.xml index 81ff88a0e0..361107c118 100644 --- a/integration-tests/jsf-app/jsf-app-standalone/pom.xml +++ b/integration-tests/jsf-app/jsf-app-standalone/pom.xml @@ -6,7 +6,7 @@ jsf-app co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT jsf-app-standalone diff --git a/integration-tests/jsf-app/pom.xml b/integration-tests/jsf-app/pom.xml index 06d3c680ef..e092d4f213 100644 --- a/integration-tests/jsf-app/pom.xml +++ b/integration-tests/jsf-app/pom.xml @@ -6,7 +6,7 @@ integration-tests co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT jsf-app diff --git a/integration-tests/main-app-test/pom.xml b/integration-tests/main-app-test/pom.xml index b9511d8f68..3d2ddd8b98 100644 --- a/integration-tests/main-app-test/pom.xml +++ b/integration-tests/main-app-test/pom.xml @@ -5,7 +5,7 @@ integration-tests co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT main-app-test diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index 6c0aa99f35..28d841ccaa 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -5,7 +5,7 @@ apm-agent-parent co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT integration-tests diff --git a/integration-tests/runtime-attach/pom.xml b/integration-tests/runtime-attach/pom.xml index 8459ec2470..a974a7c4ce 100644 --- a/integration-tests/runtime-attach/pom.xml +++ b/integration-tests/runtime-attach/pom.xml @@ -6,7 +6,7 @@ integration-tests co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT runtime-attach diff --git a/integration-tests/runtime-attach/runtime-attach-app/pom.xml b/integration-tests/runtime-attach/runtime-attach-app/pom.xml index 5768c57067..3ed3fef074 100644 --- a/integration-tests/runtime-attach/runtime-attach-app/pom.xml +++ b/integration-tests/runtime-attach/runtime-attach-app/pom.xml @@ -3,7 +3,7 @@ runtime-attach co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/integration-tests/runtime-attach/runtime-attach-test/pom.xml b/integration-tests/runtime-attach/runtime-attach-test/pom.xml index 2a8f6c52a9..dd25658026 100644 --- a/integration-tests/runtime-attach/runtime-attach-test/pom.xml +++ b/integration-tests/runtime-attach/runtime-attach-test/pom.xml @@ -3,7 +3,7 @@ runtime-attach co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT 4.0.0 diff --git a/integration-tests/simple-webapp/pom.xml b/integration-tests/simple-webapp/pom.xml index e8e1ed3c6d..7a6f4fe64b 100644 --- a/integration-tests/simple-webapp/pom.xml +++ b/integration-tests/simple-webapp/pom.xml @@ -6,7 +6,7 @@ integration-tests co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT simple-webapp diff --git a/integration-tests/soap-test/pom.xml b/integration-tests/soap-test/pom.xml index 303aa62290..151c7c95e9 100644 --- a/integration-tests/soap-test/pom.xml +++ b/integration-tests/soap-test/pom.xml @@ -5,7 +5,7 @@ integration-tests co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT soap-test diff --git a/integration-tests/spring-boot-1-5/pom.xml b/integration-tests/spring-boot-1-5/pom.xml index 37509b8a7b..6021c2975f 100644 --- a/integration-tests/spring-boot-1-5/pom.xml +++ b/integration-tests/spring-boot-1-5/pom.xml @@ -5,7 +5,7 @@ integration-tests co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT spring-boot-1-5 diff --git a/integration-tests/spring-boot-2/pom.xml b/integration-tests/spring-boot-2/pom.xml index b415a60d86..f0f9b280a1 100644 --- a/integration-tests/spring-boot-2/pom.xml +++ b/integration-tests/spring-boot-2/pom.xml @@ -5,7 +5,7 @@ integration-tests co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT spring-boot-2 diff --git a/integration-tests/spring-boot-2/spring-boot-2-base/pom.xml b/integration-tests/spring-boot-2/spring-boot-2-base/pom.xml index f179d58bf1..43ede3bb56 100644 --- a/integration-tests/spring-boot-2/spring-boot-2-base/pom.xml +++ b/integration-tests/spring-boot-2/spring-boot-2-base/pom.xml @@ -5,7 +5,7 @@ spring-boot-2 co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT spring-boot-2-base diff --git a/integration-tests/spring-boot-2/spring-boot-2-jetty/pom.xml b/integration-tests/spring-boot-2/spring-boot-2-jetty/pom.xml index 5f3a291099..6df38a8e6a 100644 --- a/integration-tests/spring-boot-2/spring-boot-2-jetty/pom.xml +++ b/integration-tests/spring-boot-2/spring-boot-2-jetty/pom.xml @@ -5,7 +5,7 @@ spring-boot-2 co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT spring-boot-2-jetty diff --git a/integration-tests/spring-boot-2/spring-boot-2-tomcat/pom.xml b/integration-tests/spring-boot-2/spring-boot-2-tomcat/pom.xml index 433ea3f104..7686cdc5e8 100644 --- a/integration-tests/spring-boot-2/spring-boot-2-tomcat/pom.xml +++ b/integration-tests/spring-boot-2/spring-boot-2-tomcat/pom.xml @@ -5,7 +5,7 @@ spring-boot-2 co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT spring-boot-2-tomcat diff --git a/integration-tests/spring-boot-2/spring-boot-2-undertow/pom.xml b/integration-tests/spring-boot-2/spring-boot-2-undertow/pom.xml index 47daf9e22b..9a6a7e6a56 100644 --- a/integration-tests/spring-boot-2/spring-boot-2-undertow/pom.xml +++ b/integration-tests/spring-boot-2/spring-boot-2-undertow/pom.xml @@ -5,7 +5,7 @@ spring-boot-2 co.elastic.apm - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT spring-boot-2-undertow diff --git a/pom.xml b/pom.xml index 848f079ed7..c4648a8353 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ co.elastic.apm apm-agent-parent - 1.29.1-SNAPSHOT + 1.30.0-SNAPSHOT pom ${project.groupId}:${project.artifactId}