This is an automated email from the ASF dual-hosted git repository.
pjfanning pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/pekko-http.git
The following commit(s) were added to refs/heads/main by this push:
new 85d7243e7 Document Opentelemetry dependencies (#1240)
85d7243e7 is described below
commit 85d7243e745d68c987b79c5c4d56958f582a5c34
Author: PJ Fanning <[email protected]>
AuthorDate: Sun Aug 30 11:59:51 2026 +0100
Document Opentelemetry dependencies (#1240)
* Document Opentelemetry dependency in startPoolInterface
Added note about Opentelemetry Java Instrumentation dependency in
startPoolInterface method.
* more doc
---
.../http/impl/engine/client/PoolMasterActor.scala | 9 ++-
.../pekko/http/impl/engine/http2/Http2.scala | 21 ++++++
.../impl/engine/server/HttpServerBluePrint.scala | 11 +++
.../org/apache/pekko/http/scaladsl/Http.scala | 14 ++++
.../http/impl/InstrumentationPointsSpec.scala | 78 ++++++++++++++++++++++
.../server/RouteInstrumentationPointsSpec.scala | 49 ++++++++++++++
.../pekko/http/scaladsl/server/Directive.scala | 7 ++
.../pekko/http/scaladsl/server/PathMatcher.scala | 15 +++++
8 files changed, 203 insertions(+), 1 deletion(-)
diff --git
a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/client/PoolMasterActor.scala
b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/client/PoolMasterActor.scala
index fd9521420..ce15c4801 100644
---
a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/client/PoolMasterActor.scala
+++
b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/client/PoolMasterActor.scala
@@ -136,11 +136,18 @@ private[http] final class PoolMasterActor extends Actor
with ActorLogging {
/**
* Start a new pool interface actor, register it in our maps, and watch its
death. No actor should
* currently exist for this pool.
- *
+ * <p>
+ * Opentelemetry Java Instrumentation relies on this method so avoid
changing it. The agent matches
+ * the mangled name
`org$apache$pekko$http$impl$engine$client$PoolMasterActor$$startPoolInterface`,
+ * which only exists in the bytecode because the closure below captures
this method.
+ * See https://github.com/apache/pekko-http/issues/1241
+ * </p>
* @param poolId the pool id this pool corresponds to
* @param fm the materializer to use for this pool
* @return the newly created actor ref
*/
+ // see
https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/6f9ca5672ce84edbbe36ce0e14386c31d68f479f/instrumentation/pekko/pekko-http-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pekkohttp/v1_0/client/PoolMasterActorInstrumentation.java
+ @noinline // Not inlined so that the agent can match the method in the
bytecode
private def startPoolInterface(poolId: PoolId)(implicit fm: Materializer):
PoolInterface = {
if (statusById.contains(poolId)) {
throw new IllegalStateException(s"pool interface actor for $poolId
already exists")
diff --git
a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/http2/Http2.scala
b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/http2/Http2.scala
index 66af36641..e4d2b3879 100644
---
a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/http2/Http2.scala
+++
b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/http2/Http2.scala
@@ -59,6 +59,13 @@ import scala.util.{ Failure, Success }
* INTERNAL API
*
* Internal entry points for Http/2 server
+ *
+ * <p>
+ * Opentelemetry Java Instrumentation relies on this class so avoid changing
it. Besides
+ * [[Http2Ext.bindAndHandleAsync]] the agent also reads the `system` member
to obtain a dispatcher for
+ * the response future, so keep that accessor around too.
+ * See https://github.com/apache/pekko-http/issues/1241
+ * </p>
*/
@InternalApi
private[http] final class Http2Ext(implicit val system: ActorSystem)
@@ -73,6 +80,13 @@ private[http] final class Http2Ext(implicit val system:
ActorSystem)
val telemetry = TelemetrySpi.create(system)
// TODO: split up similarly to what `Http` does into `serverLayer`,
`bindAndHandle`, etc.
+ /**
+ * <p>
+ * Opentelemetry Java Instrumentation relies on this method so avoid
changing it. HTTP/2 bindings do
+ * not go through `HttpExt`, so this is the only server entry point the
agent can hook for HTTP/2.
+ * See https://github.com/apache/pekko-http/issues/1241
+ * </p>
+ */
@noinline // Not inlined to permit instrumentation to pass params
(interface, port) as context to constructed implementation flows
def bindAndHandleAsync(
handler: HttpRequest => Future[HttpResponse],
@@ -298,6 +312,13 @@ private[http] final class Http2Ext(implicit val system:
ActorSystem)
@InternalApi
private[http] object Http2 extends ExtensionId[Http2Ext] with
ExtensionIdProvider {
+ /**
+ * <p>
+ * Opentelemetry Java Instrumentation relies on this attribute key so
avoid changing it. The agent uses
+ * it to recognise a request that was replayed through the HTTP/2 stack
after an h2c upgrade.
+ * See https://github.com/apache/pekko-http/issues/1241
+ * </p>
+ */
val streamId = AttributeKey[Int]("x-http2-stream-id")
override def get(system: ActorSystem): Http2Ext = super.get(system)
diff --git
a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/server/HttpServerBluePrint.scala
b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/server/HttpServerBluePrint.scala
index b29c0eb5f..e08901e36 100644
---
a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/server/HttpServerBluePrint.scala
+++
b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/server/HttpServerBluePrint.scala
@@ -101,6 +101,17 @@ private[http] object HttpServerBluePrint {
: BidiFlow[HttpResponse, ResponseRenderingContext, RequestOutput,
RequestOutput, NotUsed] =
BidiFlow.fromGraph(new ControllerStage(settings, log)).reversed
+ /**
+ * Wraps the HTTP/1.1 request/response bidi flow.
+ * <p>
+ * Opentelemetry Java Instrumentation relies on this method so avoid
changing it. The agent wraps the
+ * returned [[BidiFlow]] to start and end server spans, and matches on the
method name and on the
+ * [[BidiFlow]] return type.
+ * See https://github.com/apache/pekko-http/issues/1241
+ * </p>
+ */
+ // see
https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/6f9ca5672ce84edbbe36ce0e14386c31d68f479f/instrumentation/pekko/pekko-http-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pekkohttp/v1_0/server/HttpServerBluePrintInstrumentation.java
+ @noinline // Not inlined so that the agent can match the method in the
bytecode
def requestPreparation(
settings: ServerSettings): BidiFlow[HttpResponse, HttpResponse,
RequestOutput, HttpRequest, NotUsed] =
BidiFlow.fromFlows(Flow[HttpResponse], new PrepareRequests(settings))
diff --git a/http-core/src/main/scala/org/apache/pekko/http/scaladsl/Http.scala
b/http-core/src/main/scala/org/apache/pekko/http/scaladsl/Http.scala
index 1af6756e1..b79573dc7 100644
--- a/http-core/src/main/scala/org/apache/pekko/http/scaladsl/Http.scala
+++ b/http-core/src/main/scala/org/apache/pekko/http/scaladsl/Http.scala
@@ -589,7 +589,14 @@ class HttpExt @InternalStableApi /* constructor signature
is hardcoded in Teleme
* for setting up the HTTPS connection pool, if the request is targeted
towards an `https` endpoint.
*
* Note that the request must have an absolute URI, otherwise the future
will be completed with an error.
+ *
+ * <p>
+ * Opentelemetry Java Instrumentation relies on this method so avoid
changing it. It is the client
+ * entry point the agent instruments to create HTTP client spans and to
inject the propagated context.
+ * See https://github.com/apache/pekko-http/issues/1241
+ * </p>
*/
+ // see
https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/6f9ca5672ce84edbbe36ce0e14386c31d68f479f/instrumentation/pekko/pekko-http-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pekkohttp/v1_0/client/HttpExtClientInstrumentation.java
def singleRequest(
request: HttpRequest,
connectionContext: HttpsConnectionContext = defaultClientHttpsContext,
@@ -951,7 +958,14 @@ object Http extends ExtensionId[HttpExt] with
ExtensionIdProvider {
/**
* Handles the connection with the given flow, which is materialized
exactly once
* and the respective materialization result returned.
+ *
+ * <p>
+ * Opentelemetry Java Instrumentation relies on this method so avoid
changing it. It is the server
+ * entry point the agent instruments for bindings created from a
connection source.
+ * See https://github.com/apache/pekko-http/issues/1241
+ * </p>
*/
+ // see
https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/6f9ca5672ce84edbbe36ce0e14386c31d68f479f/instrumentation/pekko/pekko-http-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pekkohttp/v1_0/server/PekkoHttpServerSourceInstrumentation.java
def handleWith[Mat](handler: Flow[HttpRequest, HttpResponse,
Mat])(implicit fm: Materializer): Mat =
flow.joinMat(handler)(Keep.right).run()
diff --git
a/http-core/src/test/scala/org/apache/pekko/http/impl/InstrumentationPointsSpec.scala
b/http-core/src/test/scala/org/apache/pekko/http/impl/InstrumentationPointsSpec.scala
new file mode 100644
index 000000000..4e79325f0
--- /dev/null
+++
b/http-core/src/test/scala/org/apache/pekko/http/impl/InstrumentationPointsSpec.scala
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 org.apache.pekko.http.impl
+
+import java.lang.reflect.Method
+
+import org.scalatest.matchers.should.Matchers
+import org.scalatest.wordspec.AnyWordSpec
+
+/**
+ * Guards the pekko-http internals that the OpenTelemetry Java agent attaches
bytecode advice to.
+ *
+ * The agent matches these by name and signature. When a match stops applying
the instrumentation is
+ * silently disabled, so this spec fails loudly instead. Keep it in sync with
+ * https://github.com/apache/pekko-http/issues/1241
+ */
+class InstrumentationPointsSpec extends AnyWordSpec with Matchers {
+
+ private def methods(className: String): Array[Method] =
+ Class.forName(className, false, getClass.getClassLoader).getMethods
+
+ private def declaredMethods(className: String): Array[Method] =
+ Class.forName(className, false, getClass.getClassLoader).getDeclaredMethods
+
+ "The methods instrumented by the OpenTelemetry Java agent" should {
+
+ "include HttpExt.singleRequest taking an HttpRequest" in {
+ methods("org.apache.pekko.http.scaladsl.HttpExt").filter(_.getName ==
"singleRequest").exists(
+ _.getParameterTypes.headOption.exists(
+ _.getName == "org.apache.pekko.http.scaladsl.model.HttpRequest"))
shouldBe true
+ }
+
+ "include Http.IncomingConnection.handleWith taking a Flow" in {
+
methods("org.apache.pekko.http.scaladsl.Http$IncomingConnection").filter(_.getName
== "handleWith").exists(
+ _.getParameterTypes.headOption.exists(
+ _.getName == "org.apache.pekko.stream.scaladsl.Flow")) shouldBe true
+ }
+
+ "include HttpServerBluePrint.requestPreparation returning a BidiFlow" in {
+
methods("org.apache.pekko.http.impl.engine.server.HttpServerBluePrint$").filter(
+ _.getName == "requestPreparation").exists(
+ _.getReturnType.getName ==
"org.apache.pekko.stream.scaladsl.BidiFlow") shouldBe true
+ }
+
+ // the agent matches the mangled name, which only exists because a closure
in the method body captures it
+ "include the mangled accessor for PoolMasterActor.startPoolInterface" in {
+
declaredMethods("org.apache.pekko.http.impl.engine.client.PoolMasterActor").exists(
+ _.getName ==
"org$apache$pekko$http$impl$engine$client$PoolMasterActor$$startPoolInterface")
shouldBe true
+ }
+
+ "include Http2Ext.bindAndHandleAsync and Http2Ext.system" in {
+ val http2Ext =
methods("org.apache.pekko.http.impl.engine.http2.Http2Ext")
+ http2Ext.exists(_.getName == "bindAndHandleAsync") shouldBe true
+ http2Ext.filter(_.getName == "system").exists(
+ _.getReturnType.getName == "org.apache.pekko.actor.ActorSystem")
shouldBe true
+ }
+
+ "include Http2.streamId" in {
+
methods("org.apache.pekko.http.impl.engine.http2.Http2$").filter(_.getName ==
"streamId").exists(
+ _.getReturnType.getName ==
"org.apache.pekko.http.scaladsl.model.AttributeKey") shouldBe true
+ }
+ }
+}
diff --git
a/http-tests/src/test/scala/org/apache/pekko/http/scaladsl/server/RouteInstrumentationPointsSpec.scala
b/http-tests/src/test/scala/org/apache/pekko/http/scaladsl/server/RouteInstrumentationPointsSpec.scala
new file mode 100644
index 000000000..48fc61afc
--- /dev/null
+++
b/http-tests/src/test/scala/org/apache/pekko/http/scaladsl/server/RouteInstrumentationPointsSpec.scala
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 org.apache.pekko.http.scaladsl.server
+
+import org.scalatest.matchers.should.Matchers
+import org.scalatest.wordspec.AnyWordSpec
+
+/**
+ * Guards the route building blocks that the OpenTelemetry Java agent attaches
bytecode advice to in order
+ * to derive the `http.route` attribute. See
https://github.com/apache/pekko-http/issues/1241
+ */
+class RouteInstrumentationPointsSpec extends AnyWordSpec with Matchers {
+
+ "The route methods instrumented by the OpenTelemetry Java agent" should {
+
+ "include the single argument Directive.tapply" in {
+ classOf[Directive[Unit]].getMethods.filter(_.getName == "tapply").exists(
+ _.getParameterTypes.length == 1) shouldBe true
+ }
+
+ "include PathMatcher.apply taking a Uri.Path on concrete matchers" in {
+ PathMatchers.Segment.getClass.getMethods.filter(_.getName ==
"apply").exists(
+ _.getParameterTypes.headOption.exists(
+ _.getName == "org.apache.pekko.http.scaladsl.model.Uri$Path"))
shouldBe true
+ }
+
+ "include PathMatcher.apply taking a Uri.Path and returning a PathMatcher"
in {
+ PathMatcher.getClass.getMethods.filter(_.getName == "apply").exists { m
=>
+ m.getParameterTypes.headOption.exists(_.getName ==
"org.apache.pekko.http.scaladsl.model.Uri$Path") &&
+ m.getReturnType.getName ==
"org.apache.pekko.http.scaladsl.server.PathMatcher"
+ } shouldBe true
+ }
+ }
+}
diff --git
a/http/src/main/scala/org/apache/pekko/http/scaladsl/server/Directive.scala
b/http/src/main/scala/org/apache/pekko/http/scaladsl/server/Directive.scala
index ea5c50121..f20a7a5ab 100644
--- a/http/src/main/scala/org/apache/pekko/http/scaladsl/server/Directive.scala
+++ b/http/src/main/scala/org/apache/pekko/http/scaladsl/server/Directive.scala
@@ -26,6 +26,13 @@ import pekko.http.scaladsl.util.FastFuture._
/**
* A directive that provides a tuple of values of type `L` to create an inner
route.
+ *
+ * <p>
+ * Opentelemetry Java Instrumentation relies on the single-argument `tapply`
method of this class (and of
+ * every subclass) so avoid changing it. The agent uses it to derive the
`http.route` attribute.
+ * See https://github.com/apache/pekko-http/issues/1241 and
+ *
https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/6f9ca5672ce84edbbe36ce0e14386c31d68f479f/instrumentation/pekko/pekko-http-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pekkohttp/v1_0/server/route/DirectiveInstrumentation.java
+ * </p>
*/
//#basic
abstract class Directive[L](implicit val ev: Tuple[L]) {
diff --git
a/http/src/main/scala/org/apache/pekko/http/scaladsl/server/PathMatcher.scala
b/http/src/main/scala/org/apache/pekko/http/scaladsl/server/PathMatcher.scala
index 58dc010e5..6618368cc 100644
---
a/http/src/main/scala/org/apache/pekko/http/scaladsl/server/PathMatcher.scala
+++
b/http/src/main/scala/org/apache/pekko/http/scaladsl/server/PathMatcher.scala
@@ -28,6 +28,13 @@ import pekko.http.scaladsl.server.util.TupleOps._
/**
* A PathMatcher tries to match a prefix of a given string and returns either
a PathMatcher.Matched instance
* if matched, otherwise PathMatcher.Unmatched.
+ *
+ * <p>
+ * Opentelemetry Java Instrumentation relies on the `apply(Path)` method of
this class (and of every
+ * subclass) so avoid changing it. The agent uses it to derive the
`http.route` attribute.
+ * See https://github.com/apache/pekko-http/issues/1241 and
+ *
https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/6f9ca5672ce84edbbe36ce0e14386c31d68f479f/instrumentation/pekko/pekko-http-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pekkohttp/v1_0/server/route/PathMatcherStaticInstrumentation.java
+ * </p>
*/
abstract class PathMatcher[L](implicit val ev: Tuple[L]) extends (Path =>
PathMatcher.Matching[L]) { self =>
import PathMatcher._
@@ -164,7 +171,15 @@ object PathMatcher extends ImplicitPathMatcherConstruction
{
/**
* Creates a PathMatcher that matches and consumes the given path prefix and
extracts the given list of extractions.
* If the given prefix is empty the returned PathMatcher matches always and
consumes nothing.
+ *
+ * <p>
+ * Opentelemetry Java Instrumentation relies on this method so avoid
changing it. The agent matches on the
+ * method name, on the [[Path]] first parameter and on the [[PathMatcher]]
return type, and uses it to
+ * derive the `http.route` attribute.
+ * See https://github.com/apache/pekko-http/issues/1241
+ * </p>
*/
+ // see
https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/6f9ca5672ce84edbbe36ce0e14386c31d68f479f/instrumentation/pekko/pekko-http-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/pekkohttp/v1_0/server/route/PathMatcherInstrumentation.java
def apply[L](prefix: Path, extractions: L)(implicit ev: Tuple[L]):
PathMatcher[L] =
if (prefix.isEmpty) provide(extractions)
else new PathMatcher[L] {
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]