From 1170ca891cbf5758eddd3eafd72090d258e03063 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Thu, 22 Feb 2024 14:39:04 +0000 Subject: [PATCH 01/64] Extract lambda-natchez module This is to allow consumers the choice of natchez or otel4s No breaking changes except requiring a `feral.lambda.natchez._` import --- .github/workflows/ci.yml | 4 +- build.sbt | 16 +++++- .../scala/feral/examples/Http4sLambda.scala | 7 +-- .../scala/feral/examples/KinesisLambda.scala | 5 +- .../scala/feral/lambda/natchez}/AwsTags.scala | 2 +- .../feral/lambda/natchez}/KernelSource.scala | 2 +- .../feral/lambda/natchez}/TracedHandler.scala | 3 +- .../scala/feral/lambda/natchez/package.scala | 54 +++++++++++++++++++ .../lambda/natchez}/TracedHandlerSuite.scala | 4 +- .../lambda/events/ApiGatewayProxyEvent.scala | 4 -- .../events/ApiGatewayProxyEventV2.scala | 4 -- .../lambda/events/DynamoDbStreamEvent.scala | 2 - .../lambda/events/KinesisStreamEvent.scala | 2 - .../feral/lambda/events/S3BatchEvent.scala | 3 -- .../scala/feral/lambda/events/SqsEvent.scala | 5 -- 15 files changed, 85 insertions(+), 32 deletions(-) rename {lambda/shared/src/main/scala/feral/lambda => lambda-natchez/shared/src/main/scala/feral/lambda/natchez}/AwsTags.scala (96%) rename {lambda/shared/src/main/scala/feral/lambda => lambda-natchez/shared/src/main/scala/feral/lambda/natchez}/KernelSource.scala (96%) rename {lambda/shared/src/main/scala/feral/lambda => lambda-natchez/shared/src/main/scala/feral/lambda/natchez}/TracedHandler.scala (97%) create mode 100644 lambda-natchez/shared/src/main/scala/feral/lambda/natchez/package.scala rename {lambda/shared/src/test/scala/feral/lambda => lambda-natchez/shared/src/test/scala/feral/lambda/natchez}/TracedHandlerSuite.scala (93%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 92bd8fca..20c94f3a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -133,11 +133,11 @@ jobs: - name: Make target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') - run: mkdir -p lambda-cloudformation-custom-resource/.js/target lambda-http4s/.jvm/target unidocs/target lambda-http4s/.js/target lambda/js/target scalafix/rules/target lambda/jvm/target sbt-lambda/target lambda-cloudformation-custom-resource/.jvm/target project/target + run: mkdir -p lambda-natchez/jvm/target lambda-natchez/js/target lambda-cloudformation-custom-resource/.js/target lambda-http4s/.jvm/target unidocs/target lambda-http4s/.js/target lambda/js/target scalafix/rules/target lambda/jvm/target sbt-lambda/target lambda-cloudformation-custom-resource/.jvm/target project/target - name: Compress target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') - run: tar cf targets.tar lambda-cloudformation-custom-resource/.js/target lambda-http4s/.jvm/target unidocs/target lambda-http4s/.js/target lambda/js/target scalafix/rules/target lambda/jvm/target sbt-lambda/target lambda-cloudformation-custom-resource/.jvm/target project/target + run: tar cf targets.tar lambda-natchez/jvm/target lambda-natchez/js/target lambda-cloudformation-custom-resource/.js/target lambda-http4s/.jvm/target unidocs/target lambda-http4s/.js/target lambda/js/target scalafix/rules/target lambda/jvm/target sbt-lambda/target lambda-cloudformation-custom-resource/.jvm/target project/target - name: Upload target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') diff --git a/build.sbt b/build.sbt index d035b8ba..1433eef9 100644 --- a/build.sbt +++ b/build.sbt @@ -67,6 +67,7 @@ lazy val root = tlCrossRootProject .aggregate( lambda, + lambdaNatchez, lambdaHttp4s, lambdaCloudFormationCustomResource, examples, @@ -170,6 +171,19 @@ lazy val lambdaCloudFormationCustomResource = crossProject(JSPlatform, JVMPlatfo .settings(commonSettings) .dependsOn(lambda) +lazy val lambdaNatchez = crossProject(JSPlatform, JVMPlatform) + .in(file("lambda-natchez")) + .settings( + name := "feral-lambda-natchez", + libraryDependencies ++= Seq( + "org.tpolecat" %%% "natchez-core" % natchezVersion, + "org.scalameta" %%% "munit-scalacheck" % munitVersion % Test, + "org.typelevel" %%% "munit-cats-effect-3" % munitCEVersion % Test + ) + ) + .settings(commonSettings) + .dependsOn(lambda) + lazy val examples = crossProject(JSPlatform, JVMPlatform) .crossType(CrossType.Pure) .in(file("examples")) @@ -183,7 +197,7 @@ lazy val examples = crossProject(JSPlatform, JVMPlatform) ) ) .settings(commonSettings) - .dependsOn(lambda, lambdaHttp4s) + .dependsOn(lambda, lambdaNatchez, lambdaHttp4s) .enablePlugins(NoPublishPlugin) lazy val unidocs = project diff --git a/examples/src/main/scala/feral/examples/Http4sLambda.scala b/examples/src/main/scala/feral/examples/Http4sLambda.scala index 6d43807f..bf55ff48 100644 --- a/examples/src/main/scala/feral/examples/Http4sLambda.scala +++ b/examples/src/main/scala/feral/examples/Http4sLambda.scala @@ -16,14 +16,15 @@ package feral.examples +import _root_.natchez.Trace +import _root_.natchez.http4s.NatchezMiddleware +import _root_.natchez.xray.XRay import cats.effect._ import cats.effect.std.Random import feral.lambda._ import feral.lambda.events._ import feral.lambda.http4s._ -import natchez.Trace -import natchez.http4s.NatchezMiddleware -import natchez.xray.XRay +import feral.lambda.natchez._ import org.http4s.HttpApp import org.http4s.HttpRoutes import org.http4s.client.Client diff --git a/examples/src/main/scala/feral/examples/KinesisLambda.scala b/examples/src/main/scala/feral/examples/KinesisLambda.scala index 5b9decac..49cc2cb8 100644 --- a/examples/src/main/scala/feral/examples/KinesisLambda.scala +++ b/examples/src/main/scala/feral/examples/KinesisLambda.scala @@ -16,12 +16,13 @@ package feral.examples +import _root_.natchez.Trace +import _root_.natchez.xray.XRay import cats.effect._ import cats.effect.std.Random import feral.lambda._ import feral.lambda.events.SqsEvent -import natchez.Trace -import natchez.xray.XRay +import feral.lambda.natchez._ import skunk.Session /** diff --git a/lambda/shared/src/main/scala/feral/lambda/AwsTags.scala b/lambda-natchez/shared/src/main/scala/feral/lambda/natchez/AwsTags.scala similarity index 96% rename from lambda/shared/src/main/scala/feral/lambda/AwsTags.scala rename to lambda-natchez/shared/src/main/scala/feral/lambda/natchez/AwsTags.scala index b75d521b..30c1f74c 100644 --- a/lambda/shared/src/main/scala/feral/lambda/AwsTags.scala +++ b/lambda-natchez/shared/src/main/scala/feral/lambda/natchez/AwsTags.scala @@ -14,7 +14,7 @@ * limitations under the License. */ -package feral.lambda +package feral.lambda.natchez import natchez.TraceValue diff --git a/lambda/shared/src/main/scala/feral/lambda/KernelSource.scala b/lambda-natchez/shared/src/main/scala/feral/lambda/natchez/KernelSource.scala similarity index 96% rename from lambda/shared/src/main/scala/feral/lambda/KernelSource.scala rename to lambda-natchez/shared/src/main/scala/feral/lambda/natchez/KernelSource.scala index 6b0346a1..4188ef9a 100644 --- a/lambda/shared/src/main/scala/feral/lambda/KernelSource.scala +++ b/lambda-natchez/shared/src/main/scala/feral/lambda/natchez/KernelSource.scala @@ -14,7 +14,7 @@ * limitations under the License. */ -package feral.lambda +package feral.lambda.natchez import natchez.Kernel diff --git a/lambda/shared/src/main/scala/feral/lambda/TracedHandler.scala b/lambda-natchez/shared/src/main/scala/feral/lambda/natchez/TracedHandler.scala similarity index 97% rename from lambda/shared/src/main/scala/feral/lambda/TracedHandler.scala rename to lambda-natchez/shared/src/main/scala/feral/lambda/natchez/TracedHandler.scala index ac8cc28c..57c54645 100644 --- a/lambda/shared/src/main/scala/feral/lambda/TracedHandler.scala +++ b/lambda-natchez/shared/src/main/scala/feral/lambda/natchez/TracedHandler.scala @@ -14,12 +14,13 @@ * limitations under the License. */ -package feral.lambda +package feral.lambda.natchez import cats.data.Kleisli import cats.effect.IO import cats.effect.kernel.MonadCancelThrow import cats.syntax.all._ +import feral.lambda.Invocation import natchez.EntryPoint import natchez.Span import natchez.Trace diff --git a/lambda-natchez/shared/src/main/scala/feral/lambda/natchez/package.scala b/lambda-natchez/shared/src/main/scala/feral/lambda/natchez/package.scala new file mode 100644 index 00000000..c8ecd516 --- /dev/null +++ b/lambda-natchez/shared/src/main/scala/feral/lambda/natchez/package.scala @@ -0,0 +1,54 @@ +/* + * Copyright 2021 Typelevel + * + * Licensed 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 feral.lambda + +import _root_.natchez.Kernel +import feral.lambda.events.ApiGatewayProxyEvent +import feral.lambda.events.ApiGatewayProxyEventV2 +import feral.lambda.events.DynamoDbStreamEvent +import feral.lambda.events.KinesisStreamEvent +import feral.lambda.events.S3BatchEvent +import feral.lambda.events.SqsRecordAttributes +import feral.lambda.natchez.KernelSource +import org.typelevel.ci._ + +protected trait KernelSources { + private[this] val `X-Amzn-Trace-Id` = ci"X-Amzn-Trace-Id" + + implicit def apiGatewayProxyEvent: KernelSource[ApiGatewayProxyEvent] = + e => Kernel(e.headers.getOrElse(Map.empty)) + + implicit def apiGatewayProxyEventV2: KernelSource[ApiGatewayProxyEventV2] = + e => Kernel(e.headers) + + implicit def sqsRecordAttributes: KernelSource[SqsRecordAttributes] = + a => Kernel(a.awsTraceHeader.map(`X-Amzn-Trace-Id` -> _).toMap) + + implicit def s3BatchEvent: KernelSource[S3BatchEvent] = KernelSource.emptyKernelSource + + @deprecated( + "See feral.lambda.events.KinesisStreamEvent deprecation", + since = "0.3.0" + ) + implicit def kinesisStreamEvent: KernelSource[KinesisStreamEvent] = + KernelSource.emptyKernelSource + + implicit def dynamoDbStreamEvent: KernelSource[DynamoDbStreamEvent] = + KernelSource.emptyKernelSource +} + +package object natchez extends KernelSources diff --git a/lambda/shared/src/test/scala/feral/lambda/TracedHandlerSuite.scala b/lambda-natchez/shared/src/test/scala/feral/lambda/natchez/TracedHandlerSuite.scala similarity index 93% rename from lambda/shared/src/test/scala/feral/lambda/TracedHandlerSuite.scala rename to lambda-natchez/shared/src/test/scala/feral/lambda/natchez/TracedHandlerSuite.scala index b02044fe..2939fa22 100644 --- a/lambda/shared/src/test/scala/feral/lambda/TracedHandlerSuite.scala +++ b/lambda-natchez/shared/src/test/scala/feral/lambda/natchez/TracedHandlerSuite.scala @@ -14,10 +14,12 @@ * limitations under the License. */ -package feral.lambda +package feral.lambda.natchez import cats.data.Kleisli import cats.effect.IO +import feral.lambda.INothing +import feral.lambda.Invocation import feral.lambda.events.KinesisStreamEvent import natchez.EntryPoint import natchez.Span diff --git a/lambda/shared/src/main/scala/feral/lambda/events/ApiGatewayProxyEvent.scala b/lambda/shared/src/main/scala/feral/lambda/events/ApiGatewayProxyEvent.scala index e7b1fc34..435b4eb0 100644 --- a/lambda/shared/src/main/scala/feral/lambda/events/ApiGatewayProxyEvent.scala +++ b/lambda/shared/src/main/scala/feral/lambda/events/ApiGatewayProxyEvent.scala @@ -18,7 +18,6 @@ package feral.lambda package events import io.circe.Decoder -import natchez.Kernel import org.typelevel.ci.CIString sealed abstract class ApiGatewayProxyEvent { @@ -78,9 +77,6 @@ object ApiGatewayProxyEvent { "multiValueHeaders" )(ApiGatewayProxyEvent.apply) - implicit def kernelSource: KernelSource[ApiGatewayProxyEvent] = - e => Kernel(e.headers.getOrElse(Map.empty)) - private final case class Impl( body: Option[String], resource: String, diff --git a/lambda/shared/src/main/scala/feral/lambda/events/ApiGatewayProxyEventV2.scala b/lambda/shared/src/main/scala/feral/lambda/events/ApiGatewayProxyEventV2.scala index 83017a91..f0ca01b8 100644 --- a/lambda/shared/src/main/scala/feral/lambda/events/ApiGatewayProxyEventV2.scala +++ b/lambda/shared/src/main/scala/feral/lambda/events/ApiGatewayProxyEventV2.scala @@ -18,7 +18,6 @@ package feral.lambda package events import io.circe.Decoder -import natchez.Kernel import org.typelevel.ci.CIString sealed abstract class Http { @@ -86,9 +85,6 @@ object ApiGatewayProxyEventV2 { "isBase64Encoded" )(ApiGatewayProxyEventV2.apply) - implicit def kernelSource: KernelSource[ApiGatewayProxyEventV2] = - e => Kernel(e.headers) - private final case class Impl( rawPath: String, rawQueryString: String, diff --git a/lambda/shared/src/main/scala/feral/lambda/events/DynamoDbStreamEvent.scala b/lambda/shared/src/main/scala/feral/lambda/events/DynamoDbStreamEvent.scala index f9177431..6a298f82 100644 --- a/lambda/shared/src/main/scala/feral/lambda/events/DynamoDbStreamEvent.scala +++ b/lambda/shared/src/main/scala/feral/lambda/events/DynamoDbStreamEvent.scala @@ -215,8 +215,6 @@ object DynamoDbStreamEvent { implicit val decoder: Decoder[DynamoDbStreamEvent] = Decoder.forProduct1("Records")(DynamoDbStreamEvent.apply) - implicit def kernelSource: KernelSource[DynamoDbStreamEvent] = KernelSource.emptyKernelSource - private final case class Impl( records: List[DynamoDbRecord] ) extends DynamoDbStreamEvent { diff --git a/lambda/shared/src/main/scala/feral/lambda/events/KinesisStreamEvent.scala b/lambda/shared/src/main/scala/feral/lambda/events/KinesisStreamEvent.scala index bbd23757..17aea202 100644 --- a/lambda/shared/src/main/scala/feral/lambda/events/KinesisStreamEvent.scala +++ b/lambda/shared/src/main/scala/feral/lambda/events/KinesisStreamEvent.scala @@ -146,8 +146,6 @@ object KinesisStreamEvent { implicit val decoder: Decoder[KinesisStreamEvent] = Decoder.forProduct1("Records")(KinesisStreamEvent.apply) - implicit def kernelSource: KernelSource[KinesisStreamEvent] = KernelSource.emptyKernelSource - private final case class Impl( records: List[KinesisStreamRecord] ) extends KinesisStreamEvent { diff --git a/lambda/shared/src/main/scala/feral/lambda/events/S3BatchEvent.scala b/lambda/shared/src/main/scala/feral/lambda/events/S3BatchEvent.scala index 864889d6..be44098c 100644 --- a/lambda/shared/src/main/scala/feral/lambda/events/S3BatchEvent.scala +++ b/lambda/shared/src/main/scala/feral/lambda/events/S3BatchEvent.scala @@ -16,7 +16,6 @@ package feral.lambda.events -import feral.lambda.KernelSource import io.circe.Decoder // https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/aws-lambda/trigger/s3-batch.d.ts @@ -41,8 +40,6 @@ object S3BatchEvent { Decoder.forProduct4("invocationSchemaVersion", "invocationId", "job", "tasks")( S3BatchEvent.apply) - implicit def kernelSource: KernelSource[S3BatchEvent] = KernelSource.emptyKernelSource - private final case class Impl( invocationSchemaVersion: String, invocationId: String, diff --git a/lambda/shared/src/main/scala/feral/lambda/events/SqsEvent.scala b/lambda/shared/src/main/scala/feral/lambda/events/SqsEvent.scala index cc5271f5..343f0449 100644 --- a/lambda/shared/src/main/scala/feral/lambda/events/SqsEvent.scala +++ b/lambda/shared/src/main/scala/feral/lambda/events/SqsEvent.scala @@ -19,7 +19,6 @@ package events import io.circe.Decoder import io.circe.scodec._ -import natchez.Kernel import org.typelevel.ci._ import scodec.bits.ByteVector @@ -176,9 +175,6 @@ object SqsRecordAttributes { ) } - implicit def kernelSource: KernelSource[SqsRecordAttributes] = a => - Kernel(a.awsTraceHeader.map(`X-Amzn-Trace-Id` -> _).toMap) - private final case class Impl( awsTraceHeader: Option[String], approximateReceiveCount: String, @@ -192,7 +188,6 @@ object SqsRecordAttributes { override def productPrefix = "SqsRecordAttributes" } - private[this] val `X-Amzn-Trace-Id` = ci"X-Amzn-Trace-Id" } sealed abstract class SqsMessageAttribute From 09d412704a923b731f17fbbd6ed37626cc61d3c4 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Thu, 22 Feb 2024 15:00:30 +0000 Subject: [PATCH 02/64] Remove natchez-core dependency from lambda module --- build.sbt | 2 +- examples/src/main/scala/feral/examples/KinesisLambda.scala | 1 - lambda/shared/src/main/scala/feral/lambda/events/SqsEvent.scala | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/build.sbt b/build.sbt index 1433eef9..2f95332d 100644 --- a/build.sbt +++ b/build.sbt @@ -89,7 +89,7 @@ lazy val lambda = crossProject(JSPlatform, JVMPlatform) name := "feral-lambda", libraryDependencies ++= Seq( "org.typelevel" %%% "cats-effect" % catsEffectVersion, - "org.tpolecat" %%% "natchez-core" % natchezVersion, + "org.typelevel" %%% "case-insensitive" % "1.4.0", "io.circe" %%% "circe-scodec" % circeVersion, "io.circe" %%% "circe-jawn" % circeVersion, "com.comcast" %%% "ip4s-core" % "3.5.0", diff --git a/examples/src/main/scala/feral/examples/KinesisLambda.scala b/examples/src/main/scala/feral/examples/KinesisLambda.scala index 49cc2cb8..06599c5c 100644 --- a/examples/src/main/scala/feral/examples/KinesisLambda.scala +++ b/examples/src/main/scala/feral/examples/KinesisLambda.scala @@ -22,7 +22,6 @@ import cats.effect._ import cats.effect.std.Random import feral.lambda._ import feral.lambda.events.SqsEvent -import feral.lambda.natchez._ import skunk.Session /** diff --git a/lambda/shared/src/main/scala/feral/lambda/events/SqsEvent.scala b/lambda/shared/src/main/scala/feral/lambda/events/SqsEvent.scala index 343f0449..d2f05067 100644 --- a/lambda/shared/src/main/scala/feral/lambda/events/SqsEvent.scala +++ b/lambda/shared/src/main/scala/feral/lambda/events/SqsEvent.scala @@ -19,7 +19,6 @@ package events import io.circe.Decoder import io.circe.scodec._ -import org.typelevel.ci._ import scodec.bits.ByteVector import java.time.Instant From 375b814db46bf332466c6f519e91238277370e42 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Fri, 23 Feb 2024 15:31:48 +0000 Subject: [PATCH 03/64] Add lambda-otel4s module --- .github/workflows/ci.yml | 4 +- build.sbt | 21 +++++ .../feral/lambda/otel4s/FaasAttributes.scala | 32 ++++++++ .../feral/lambda/otel4s/TracedHandler.scala | 78 +++++++++++++++++++ .../scala/feral/lambda/otel4s/package.scala | 59 ++++++++++++++ 5 files changed, 192 insertions(+), 2 deletions(-) create mode 100644 lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/FaasAttributes.scala create mode 100644 lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala create mode 100644 lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/package.scala diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 20c94f3a..253619b3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -133,11 +133,11 @@ jobs: - name: Make target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') - run: mkdir -p lambda-natchez/jvm/target lambda-natchez/js/target lambda-cloudformation-custom-resource/.js/target lambda-http4s/.jvm/target unidocs/target lambda-http4s/.js/target lambda/js/target scalafix/rules/target lambda/jvm/target sbt-lambda/target lambda-cloudformation-custom-resource/.jvm/target project/target + run: mkdir -p lambda-natchez/jvm/target lambda-natchez/js/target lambda-cloudformation-custom-resource/.js/target lambda-http4s/.jvm/target unidocs/target lambda-http4s/.js/target lambda/js/target scalafix/rules/target lambda/jvm/target lambda-otel4s/jvm/target sbt-lambda/target lambda-otel4s/js/target lambda-cloudformation-custom-resource/.jvm/target project/target - name: Compress target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') - run: tar cf targets.tar lambda-natchez/jvm/target lambda-natchez/js/target lambda-cloudformation-custom-resource/.js/target lambda-http4s/.jvm/target unidocs/target lambda-http4s/.js/target lambda/js/target scalafix/rules/target lambda/jvm/target sbt-lambda/target lambda-cloudformation-custom-resource/.jvm/target project/target + run: tar cf targets.tar lambda-natchez/jvm/target lambda-natchez/js/target lambda-cloudformation-custom-resource/.js/target lambda-http4s/.jvm/target unidocs/target lambda-http4s/.js/target lambda/js/target scalafix/rules/target lambda/jvm/target lambda-otel4s/jvm/target sbt-lambda/target lambda-otel4s/js/target lambda-cloudformation-custom-resource/.jvm/target project/target - name: Upload target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') diff --git a/build.sbt b/build.sbt index 2f95332d..683cbf1e 100644 --- a/build.sbt +++ b/build.sbt @@ -15,9 +15,15 @@ */ import com.typesafe.tools.mima.core._ +import xerial.sbt.Sonatype.sonatype01 name := "feral" +// TODO remove +// ThisBuild / sonatypeCredentialHost := sonatype01 +ThisBuild / resolvers ++= Resolver.sonatypeOssRepos("snapshots") +// ThisBuild / resolvers += sonatypeStagingResolver.value + ThisBuild / tlBaseVersion := "0.3" ThisBuild / startYear := Some(2021) @@ -68,6 +74,7 @@ lazy val root = .aggregate( lambda, lambdaNatchez, + lambdaOtel4s, lambdaHttp4s, lambdaCloudFormationCustomResource, examples, @@ -184,6 +191,20 @@ lazy val lambdaNatchez = crossProject(JSPlatform, JVMPlatform) .settings(commonSettings) .dependsOn(lambda) +lazy val lambdaOtel4s = crossProject(JSPlatform, JVMPlatform) + .in(file("lambda-otel4s")) + .settings( + name := "feral-lambda-otel4s", + libraryDependencies ++= Seq( + "org.typelevel" %%% "otel4s-core-trace" % "0.5-04ec3c4-SNAPSHOT", + "org.typelevel" %%% "otel4s-semconv" % "0.5-04ec3c4-SNAPSHOT", + "org.scalameta" %%% "munit-scalacheck" % munitVersion % Test, + "org.typelevel" %%% "munit-cats-effect-3" % munitCEVersion % Test + ) + ) + .settings(commonSettings) + .dependsOn(lambda) + lazy val examples = crossProject(JSPlatform, JVMPlatform) .crossType(CrossType.Pure) .in(file("examples")) diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/FaasAttributes.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/FaasAttributes.scala new file mode 100644 index 00000000..c0940a9e --- /dev/null +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/FaasAttributes.scala @@ -0,0 +1,32 @@ +/* + * Copyright 2021 Typelevel + * + * Licensed 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 feral.lambda.otel4s + +import org.typelevel.otel4s.semconv.resource.attributes.ResourceAttributes +import org.typelevel.otel4s.semconv.trace.attributes.SemanticAttributes + +object LambdaContextAttributes { + val InvocationId = SemanticAttributes.FaasInvocationId + val FaasTrigger = SemanticAttributes.FaasTrigger + // ARN + val CloudResourceId = ResourceAttributes.CloudResourceId + // log stream name + val FaasInstance = ResourceAttributes.FaasInstance + val FaasMaxMemory = ResourceAttributes.FaasMaxMemory + val FaasName = ResourceAttributes.FaasName + val FaasVersion = ResourceAttributes.FaasVersion +} diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala new file mode 100644 index 00000000..929b5e1b --- /dev/null +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala @@ -0,0 +1,78 @@ +/* + * Copyright 2021 Typelevel + * + * Licensed 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 feral.lambda.otel4s + +import cats.Monad +import cats.data.Kleisli +import cats.syntax.all._ +import feral.lambda.Context +import feral.lambda.Invocation +import org.typelevel.otel4s.Attribute +import org.typelevel.otel4s.semconv.resource.attributes.ResourceAttributes +import org.typelevel.otel4s.trace.Tracer + +object TracedHandler { + + def apply[F[_]: Monad: Tracer, Event, Result]( + handler: Kleisli[F, Event, Option[Result]] + )( + implicit inv: Invocation[F, Event], + etc: EventTraceContext[Event] + ): F[Option[Result]] = + for { + event <- inv.event + context <- inv.context + res <- Tracer[F].joinOrRoot(etc.contextCarrier(event)) { + val spanR = + Tracer[F] + .spanBuilder(context.functionName) + .withSpanKind(etc.spanKind) + .addAttributes(staticAttributes) + .addAttributes(contextAttrs(context)) + .addAttributes(etc.attributes(event)) + .build + + spanR.surround { + for { + res <- handler(event) + } yield res + } + } + } yield res + + private def contextAttrs[F[_]](context: Context[F]): List[Attribute[_]] = { + import LambdaContextAttributes._ + + List( + CloudResourceId(context.invokedFunctionArn), + FaasInstance(context.logStreamName), + FaasMaxMemory(context.memoryLimitInMB.toLong), + FaasName(context.functionName), + FaasVersion(context.functionVersion) + ) + } + + private def staticAttributes: List[Attribute[_]] = { + import ResourceAttributes._ + + List( + CloudProvider( + ResourceAttributes.CloudProviderValue.Aws.value + ) + ) + } +} diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/package.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/package.scala new file mode 100644 index 00000000..5e2458a8 --- /dev/null +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/package.scala @@ -0,0 +1,59 @@ +/* + * Copyright 2021 Typelevel + * + * Licensed 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 feral.lambda.otel4s + +import feral.lambda.events.SqsMessageAttribute +import feral.lambda.events.SqsRecord +import org.typelevel.otel4s.Attribute +import org.typelevel.otel4s.semconv.trace.attributes.SemanticAttributes +import org.typelevel.otel4s.semconv.trace.attributes.SemanticAttributes.FaasTriggerValue +import org.typelevel.otel4s.trace.SpanKind + +protected trait EventTraceContext[E] { + def contextCarrier(e: E): Map[String, String] + def spanKind: SpanKind + def attributes(e: E): List[Attribute[_]] +} + +package object otel4s { + implicit def sqsRecordEventTextMap: EventTraceContext[SqsRecord] = + new EventTraceContext[SqsRecord] { + def contextCarrier(e: SqsRecord): Map[String, String] = + e.messageAttributes.collect { case (k, SqsMessageAttribute.String(v)) => k -> v } + def spanKind: SpanKind = SpanKind.Consumer + def attributes(e: SqsRecord): List[Attribute[_]] = { + List( + SemanticAttributes.FaasTrigger(FaasTriggerValue.Pubsub.value) + ) + } + } + +} + +object EventSemanticAttributes { + def sqsRecord(e: SqsRecord): List[Attribute[_]] = { + import SemanticAttributes._ + + List( + FaasTrigger(FaasTriggerValue.Pubsub.value), + MessagingSystem("aws_sqs"), + MessagingOperation(MessagingOperationValue.Receive.value), + MessagingMessageId(e.messageId), + MessagingDestinationName(e.eventSource), + ) + } +} From 7ebde299544fd847d1cfc36f0f90a63891f7fa5d Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Fri, 23 Feb 2024 16:19:44 +0000 Subject: [PATCH 04/64] Use SqsEvent for attributes, not SqsRecord --- .../feral/lambda/otel4s/FaasAttributes.scala | 15 ++++++++ .../feral/lambda/otel4s/TracedHandler.scala | 33 +++-------------- .../scala/feral/lambda/otel4s/package.scala | 36 +++++++++++-------- 3 files changed, 42 insertions(+), 42 deletions(-) diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/FaasAttributes.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/FaasAttributes.scala index c0940a9e..cc908d18 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/FaasAttributes.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/FaasAttributes.scala @@ -16,6 +16,8 @@ package feral.lambda.otel4s +import feral.lambda.Context +import org.typelevel.otel4s.Attribute import org.typelevel.otel4s.semconv.resource.attributes.ResourceAttributes import org.typelevel.otel4s.semconv.trace.attributes.SemanticAttributes @@ -29,4 +31,17 @@ object LambdaContextAttributes { val FaasMaxMemory = ResourceAttributes.FaasMaxMemory val FaasName = ResourceAttributes.FaasName val FaasVersion = ResourceAttributes.FaasVersion + val CloudProvider = ResourceAttributes.CloudProvider + + def apply[F[_]](context: Context[F]): List[Attribute[_]] = { + List( + CloudProvider(ResourceAttributes.CloudProviderValue.Aws.value), + CloudResourceId(context.invokedFunctionArn), + FaasInstance(context.logStreamName), + FaasMaxMemory(context.memoryLimitInMB.toLong), + FaasName(context.functionName), + FaasVersion(context.functionVersion) + ) + } + } diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala index 929b5e1b..0c89f74f 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala @@ -31,19 +31,18 @@ object TracedHandler { handler: Kleisli[F, Event, Option[Result]] )( implicit inv: Invocation[F, Event], - etc: EventTraceContext[Event] + esa: EventSpanAttributes[Event] ): F[Option[Result]] = for { event <- inv.event context <- inv.context - res <- Tracer[F].joinOrRoot(etc.contextCarrier(event)) { + res <- Tracer[F].joinOrRoot(esa.contextCarrier(event)) { val spanR = Tracer[F] .spanBuilder(context.functionName) - .withSpanKind(etc.spanKind) - .addAttributes(staticAttributes) - .addAttributes(contextAttrs(context)) - .addAttributes(etc.attributes(event)) + .addAttributes(LambdaContextAttributes(context)) + .withSpanKind(esa.spanKind) + .addAttributes(esa.attributes(event)) .build spanR.surround { @@ -53,26 +52,4 @@ object TracedHandler { } } } yield res - - private def contextAttrs[F[_]](context: Context[F]): List[Attribute[_]] = { - import LambdaContextAttributes._ - - List( - CloudResourceId(context.invokedFunctionArn), - FaasInstance(context.logStreamName), - FaasMaxMemory(context.memoryLimitInMB.toLong), - FaasName(context.functionName), - FaasVersion(context.functionVersion) - ) - } - - private def staticAttributes: List[Attribute[_]] = { - import ResourceAttributes._ - - List( - CloudProvider( - ResourceAttributes.CloudProviderValue.Aws.value - ) - ) - } } diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/package.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/package.scala index 5e2458a8..3a40e179 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/package.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/package.scala @@ -22,38 +22,46 @@ import org.typelevel.otel4s.Attribute import org.typelevel.otel4s.semconv.trace.attributes.SemanticAttributes import org.typelevel.otel4s.semconv.trace.attributes.SemanticAttributes.FaasTriggerValue import org.typelevel.otel4s.trace.SpanKind +import feral.lambda.events.SqsEvent -protected trait EventTraceContext[E] { +protected trait EventSpanAttributes[E] { def contextCarrier(e: E): Map[String, String] def spanKind: SpanKind def attributes(e: E): List[Attribute[_]] } package object otel4s { - implicit def sqsRecordEventTextMap: EventTraceContext[SqsRecord] = - new EventTraceContext[SqsRecord] { - def contextCarrier(e: SqsRecord): Map[String, String] = - e.messageAttributes.collect { case (k, SqsMessageAttribute.String(v)) => k -> v } + implicit def sqsEventSpanAttributes: EventSpanAttributes[SqsEvent] = + new EventSpanAttributes[SqsEvent] { + def contextCarrier(e: SqsEvent): Map[String, String] = + Map.empty def spanKind: SpanKind = SpanKind.Consumer - def attributes(e: SqsRecord): List[Attribute[_]] = { - List( - SemanticAttributes.FaasTrigger(FaasTriggerValue.Pubsub.value) - ) - } + def attributes(e: SqsEvent): List[Attribute[_]] = + SqsEventAttributes() } } -object EventSemanticAttributes { - def sqsRecord(e: SqsRecord): List[Attribute[_]] = { +object SqsEventAttributes { + def apply(): List[Attribute[_]] = { + import SemanticAttributes._ + + List( + FaasTrigger(FaasTriggerValue.Pubsub.value), + MessagingSystem("aws_sqs") + ) + } +} + +object SqsRecordAttributes { + def apply(e: SqsRecord): List[Attribute[_]] = { import SemanticAttributes._ List( FaasTrigger(FaasTriggerValue.Pubsub.value), - MessagingSystem("aws_sqs"), MessagingOperation(MessagingOperationValue.Receive.value), MessagingMessageId(e.messageId), - MessagingDestinationName(e.eventSource), + MessagingDestinationName(e.eventSource) ) } } From a5506a468274bec5e8655853245b10ef0af28231 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Fri, 23 Feb 2024 17:17:08 +0000 Subject: [PATCH 05/64] Add otel4s-sdk example --- build.sbt | 10 +-- .../scala/feral/examples/SqsOtelExample.scala | 63 +++++++++++++++++++ .../feral/lambda/otel4s/EventAttributes.scala | 29 +++++++++ .../lambda/otel4s/EventSpanAttributes.scala | 10 +++ .../feral/lambda/otel4s/FaasAttributes.scala | 2 +- .../feral/lambda/otel4s/TracedHandler.scala | 16 ++--- .../scala/feral/lambda/otel4s/package.scala | 40 ++---------- 7 files changed, 119 insertions(+), 51 deletions(-) create mode 100644 examples/src/main/scala/feral/examples/SqsOtelExample.scala create mode 100644 lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala create mode 100644 lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventSpanAttributes.scala diff --git a/build.sbt b/build.sbt index 683cbf1e..a5516725 100644 --- a/build.sbt +++ b/build.sbt @@ -64,6 +64,7 @@ val natchezVersion = "0.3.5" val munitVersion = "0.7.29" val munitCEVersion = "1.0.7" val scalacheckEffectVersion = "1.0.4" +val otel4sVersion = "0.5-04ec3c4-20240223T163935Z-SNAPSHOT" lazy val commonSettings = Seq( crossScalaVersions := Seq(Scala3, Scala213) @@ -196,8 +197,8 @@ lazy val lambdaOtel4s = crossProject(JSPlatform, JVMPlatform) .settings( name := "feral-lambda-otel4s", libraryDependencies ++= Seq( - "org.typelevel" %%% "otel4s-core-trace" % "0.5-04ec3c4-SNAPSHOT", - "org.typelevel" %%% "otel4s-semconv" % "0.5-04ec3c4-SNAPSHOT", + "org.typelevel" %%% "otel4s-core-trace" % otel4sVersion, + "org.typelevel" %%% "otel4s-semconv" % otel4sVersion, "org.scalameta" %%% "munit-scalacheck" % munitVersion % Test, "org.typelevel" %%% "munit-cats-effect-3" % munitCEVersion % Test ) @@ -214,11 +215,12 @@ lazy val examples = crossProject(JSPlatform, JVMPlatform) "org.http4s" %%% "http4s-ember-client" % http4sVersion, "org.tpolecat" %%% "natchez-xray" % natchezVersion, "org.tpolecat" %%% "natchez-http4s" % "0.5.0", - "org.tpolecat" %%% "skunk-core" % "0.6.3" + "org.tpolecat" %%% "skunk-core" % "0.6.3", + "org.typelevel" %%% "otel4s-sdk" % otel4sVersion, ) ) .settings(commonSettings) - .dependsOn(lambda, lambdaNatchez, lambdaHttp4s) + .dependsOn(lambda, lambdaNatchez, lambdaHttp4s, lambdaOtel4s) .enablePlugins(NoPublishPlugin) lazy val unidocs = project diff --git a/examples/src/main/scala/feral/examples/SqsOtelExample.scala b/examples/src/main/scala/feral/examples/SqsOtelExample.scala new file mode 100644 index 00000000..ea7d9a80 --- /dev/null +++ b/examples/src/main/scala/feral/examples/SqsOtelExample.scala @@ -0,0 +1,63 @@ +package feral.examples + +import cats.syntax.all._ +import org.typelevel.otel4s.sdk.OpenTelemetrySdk +import feral.lambda.IOLambda +import cats.effect.IO +import feral.lambda.events.SqsEvent +import feral.lambda.INothing +import feral.lambda.Invocation +import org.typelevel.otel4s.trace.Tracer +import org.http4s.client.Client +import org.typelevel.scalaccompat.annotation.unused +import org.http4s.ember.client.EmberClientBuilder +import cats.Monad +import feral.lambda.events.SqsRecord +import feral.lambda.otel4s.SqsRecordTraceAttributes +import feral.lambda.otel4s.TracedHandler +import feral.lambda.otel4s.implicits._ + +object SqsOtelExample extends IOLambda[SqsEvent, INothing] { + + def handler = { + OpenTelemetrySdk + .autoConfigured[IO]() + .map(_.sdk) + .evalMap(_.tracerProvider.get("feral.examples.SqsOtelExample")) + .flatMap { implicit tracer: Tracer[IO] => + for { + client <- EmberClientBuilder.default[IO].build + tracedClient = clientTraceMiddleware(client) + } yield { implicit inv: Invocation[IO, SqsEvent] => + TracedHandler[IO, SqsEvent, INothing]( + handleEvent[IO](tracedClient) + ) + } + } + } + + def handleEvent[F[_]: Monad: Tracer]( + @unused client: Client[F] + ): SqsEvent => F[Option[INothing]] = { event => + event + .records + .traverse(record => + Tracer[F].span("handle-record", SqsRecordTraceAttributes(record)).surround { + handleRecord[F](record) + }) + .as(None) + } + + def handleRecord[F[_]: Monad: Tracer](@unused record: SqsRecord): F[Unit] = { + Tracer[F].span("some-operation").surround { + Monad[F].unit + } + } + + // stub client middleware while http4s-otel-middleware catches up to otel4s + // 0.5 + private def clientTraceMiddleware[F[_]](client: Client[F])(implicit @unused T: Tracer[IO]) = { + client + } + +} diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala new file mode 100644 index 00000000..3db8134c --- /dev/null +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala @@ -0,0 +1,29 @@ +package feral.lambda.otel4s + +import feral.lambda.events.SqsRecord +import org.typelevel.otel4s.Attribute +import org.typelevel.otel4s.semconv.trace.attributes.SemanticAttributes + +object SqsEventTraceAttributes { + def apply(): List[Attribute[_]] = { + import SemanticAttributes._ + + List( + FaasTrigger(FaasTriggerValue.Pubsub.value), + MessagingSystem("aws_sqs") + ) + } +} + +object SqsRecordTraceAttributes { + def apply(e: SqsRecord): List[Attribute[_]] = { + import SemanticAttributes._ + + List( + FaasTrigger(FaasTriggerValue.Pubsub.value), + MessagingOperation(MessagingOperationValue.Receive.value), + MessagingMessageId(e.messageId), + MessagingDestinationName(e.eventSource) + ) + } +} diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventSpanAttributes.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventSpanAttributes.scala new file mode 100644 index 00000000..db5af77a --- /dev/null +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventSpanAttributes.scala @@ -0,0 +1,10 @@ +package feral.lambda.otel4s + +import org.typelevel.otel4s.Attribute +import org.typelevel.otel4s.trace.SpanKind + +protected trait EventSpanAttributes[E] { + def contextCarrier(e: E): Map[String, String] + def spanKind: SpanKind + def attributes(e: E): List[Attribute[_]] +} diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/FaasAttributes.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/FaasAttributes.scala index cc908d18..060a0e6a 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/FaasAttributes.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/FaasAttributes.scala @@ -21,7 +21,7 @@ import org.typelevel.otel4s.Attribute import org.typelevel.otel4s.semconv.resource.attributes.ResourceAttributes import org.typelevel.otel4s.semconv.trace.attributes.SemanticAttributes -object LambdaContextAttributes { +object LambdaContextTraceAttributes { val InvocationId = SemanticAttributes.FaasInvocationId val FaasTrigger = SemanticAttributes.FaasTrigger // ARN diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala index 0c89f74f..6bc0d710 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala @@ -17,32 +17,28 @@ package feral.lambda.otel4s import cats.Monad -import cats.data.Kleisli import cats.syntax.all._ -import feral.lambda.Context import feral.lambda.Invocation -import org.typelevel.otel4s.Attribute -import org.typelevel.otel4s.semconv.resource.attributes.ResourceAttributes import org.typelevel.otel4s.trace.Tracer object TracedHandler { def apply[F[_]: Monad: Tracer, Event, Result]( - handler: Kleisli[F, Event, Option[Result]] + handler: Event => F[Option[Result]] )( implicit inv: Invocation[F, Event], - esa: EventSpanAttributes[Event] + attr: EventSpanAttributes[Event] ): F[Option[Result]] = for { event <- inv.event context <- inv.context - res <- Tracer[F].joinOrRoot(esa.contextCarrier(event)) { + res <- Tracer[F].joinOrRoot(attr.contextCarrier(event)) { val spanR = Tracer[F] .spanBuilder(context.functionName) - .addAttributes(LambdaContextAttributes(context)) - .withSpanKind(esa.spanKind) - .addAttributes(esa.attributes(event)) + .addAttributes(LambdaContextTraceAttributes(context)) + .withSpanKind(attr.spanKind) + .addAttributes(attr.attributes(event)) .build spanR.surround { diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/package.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/package.scala index 3a40e179..ea91452d 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/package.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/package.scala @@ -16,52 +16,20 @@ package feral.lambda.otel4s -import feral.lambda.events.SqsMessageAttribute -import feral.lambda.events.SqsRecord import org.typelevel.otel4s.Attribute -import org.typelevel.otel4s.semconv.trace.attributes.SemanticAttributes -import org.typelevel.otel4s.semconv.trace.attributes.SemanticAttributes.FaasTriggerValue import org.typelevel.otel4s.trace.SpanKind import feral.lambda.events.SqsEvent -protected trait EventSpanAttributes[E] { - def contextCarrier(e: E): Map[String, String] - def spanKind: SpanKind - def attributes(e: E): List[Attribute[_]] -} - -package object otel4s { +package object implicits { implicit def sqsEventSpanAttributes: EventSpanAttributes[SqsEvent] = new EventSpanAttributes[SqsEvent] { def contextCarrier(e: SqsEvent): Map[String, String] = Map.empty + def spanKind: SpanKind = SpanKind.Consumer + def attributes(e: SqsEvent): List[Attribute[_]] = - SqsEventAttributes() + SqsEventTraceAttributes() } - } -object SqsEventAttributes { - def apply(): List[Attribute[_]] = { - import SemanticAttributes._ - - List( - FaasTrigger(FaasTriggerValue.Pubsub.value), - MessagingSystem("aws_sqs") - ) - } -} - -object SqsRecordAttributes { - def apply(e: SqsRecord): List[Attribute[_]] = { - import SemanticAttributes._ - - List( - FaasTrigger(FaasTriggerValue.Pubsub.value), - MessagingOperation(MessagingOperationValue.Receive.value), - MessagingMessageId(e.messageId), - MessagingDestinationName(e.eventSource) - ) - } -} From a17882b862fd2fa34071b4a0ef8b1ab6691e14a8 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Fri, 23 Feb 2024 17:24:33 +0000 Subject: [PATCH 06/64] re-run prePR --- .../scala/feral/examples/SqsOtelExample.scala | 34 ++++++++++++++----- .../feral/lambda/otel4s/EventAttributes.scala | 16 +++++++++ .../lambda/otel4s/EventSpanAttributes.scala | 16 +++++++++ .../scala/feral/lambda/otel4s/package.scala | 5 ++- 4 files changed, 59 insertions(+), 12 deletions(-) diff --git a/examples/src/main/scala/feral/examples/SqsOtelExample.scala b/examples/src/main/scala/feral/examples/SqsOtelExample.scala index ea7d9a80..74b85d2c 100644 --- a/examples/src/main/scala/feral/examples/SqsOtelExample.scala +++ b/examples/src/main/scala/feral/examples/SqsOtelExample.scala @@ -1,21 +1,37 @@ +/* + * Copyright 2021 Typelevel + * + * Licensed 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 feral.examples -import cats.syntax.all._ -import org.typelevel.otel4s.sdk.OpenTelemetrySdk -import feral.lambda.IOLambda +import cats.Monad import cats.effect.IO -import feral.lambda.events.SqsEvent +import cats.syntax.all._ import feral.lambda.INothing +import feral.lambda.IOLambda import feral.lambda.Invocation -import org.typelevel.otel4s.trace.Tracer -import org.http4s.client.Client -import org.typelevel.scalaccompat.annotation.unused -import org.http4s.ember.client.EmberClientBuilder -import cats.Monad +import feral.lambda.events.SqsEvent import feral.lambda.events.SqsRecord import feral.lambda.otel4s.SqsRecordTraceAttributes import feral.lambda.otel4s.TracedHandler import feral.lambda.otel4s.implicits._ +import org.http4s.client.Client +import org.http4s.ember.client.EmberClientBuilder +import org.typelevel.otel4s.sdk.OpenTelemetrySdk +import org.typelevel.otel4s.trace.Tracer +import org.typelevel.scalaccompat.annotation.unused object SqsOtelExample extends IOLambda[SqsEvent, INothing] { diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala index 3db8134c..ea8345c1 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2021 Typelevel + * + * Licensed 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 feral.lambda.otel4s import feral.lambda.events.SqsRecord diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventSpanAttributes.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventSpanAttributes.scala index db5af77a..9603cac2 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventSpanAttributes.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventSpanAttributes.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2021 Typelevel + * + * Licensed 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 feral.lambda.otel4s import org.typelevel.otel4s.Attribute diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/package.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/package.scala index ea91452d..84f226e8 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/package.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/package.scala @@ -16,9 +16,9 @@ package feral.lambda.otel4s +import feral.lambda.events.SqsEvent import org.typelevel.otel4s.Attribute import org.typelevel.otel4s.trace.SpanKind -import feral.lambda.events.SqsEvent package object implicits { implicit def sqsEventSpanAttributes: EventSpanAttributes[SqsEvent] = @@ -28,8 +28,7 @@ package object implicits { def spanKind: SpanKind = SpanKind.Consumer - def attributes(e: SqsEvent): List[Attribute[_]] = + def attributes(e: SqsEvent): List[Attribute[_]] = SqsEventTraceAttributes() } } - From c596060f3797937db12d553007f4adc3e2b4c2ba Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Fri, 23 Feb 2024 17:35:01 +0000 Subject: [PATCH 07/64] Remove resolvers --- build.sbt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/build.sbt b/build.sbt index a5516725..596b313f 100644 --- a/build.sbt +++ b/build.sbt @@ -19,11 +19,6 @@ import xerial.sbt.Sonatype.sonatype01 name := "feral" -// TODO remove -// ThisBuild / sonatypeCredentialHost := sonatype01 -ThisBuild / resolvers ++= Resolver.sonatypeOssRepos("snapshots") -// ThisBuild / resolvers += sonatypeStagingResolver.value - ThisBuild / tlBaseVersion := "0.3" ThisBuild / startYear := Some(2021) From 88fa15ac06b0e900febccaa99b6a5a0f33070741 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Tue, 27 Feb 2024 18:26:03 +0000 Subject: [PATCH 08/64] Add otel4s TracedHandler tests --- build.sbt | 1 + .../lambda/otel4s/TracedHandlerSuite.scala | 142 ++++++++++++++++++ .../feral/lambda/otel4s/TracedHandler.scala | 22 +-- 3 files changed, 156 insertions(+), 9 deletions(-) create mode 100644 lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala diff --git a/build.sbt b/build.sbt index 596b313f..929765ec 100644 --- a/build.sbt +++ b/build.sbt @@ -194,6 +194,7 @@ lazy val lambdaOtel4s = crossProject(JSPlatform, JVMPlatform) libraryDependencies ++= Seq( "org.typelevel" %%% "otel4s-core-trace" % otel4sVersion, "org.typelevel" %%% "otel4s-semconv" % otel4sVersion, + "org.typelevel" %%% "otel4s-sdk-trace-testkit" % otel4sVersion % Test, "org.scalameta" %%% "munit-scalacheck" % munitVersion % Test, "org.typelevel" %%% "munit-cats-effect-3" % munitCEVersion % Test ) diff --git a/lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala b/lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala new file mode 100644 index 00000000..d2b8e087 --- /dev/null +++ b/lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala @@ -0,0 +1,142 @@ +package feral.lambda +package otel4s + +import cats.effect.IO +import cats.effect.kernel.Resource +import cats.syntax.all._ +import feral.lambda.IOLambda +import io.circe.Decoder +import io.circe.Encoder +import io.circe.scalajs._ +import munit.CatsEffectSuite +import org.typelevel.otel4s.Attribute +import org.typelevel.otel4s.sdk.testkit.trace.TracesTestkit +import org.typelevel.otel4s.trace.SpanKind + +import java.util.concurrent.atomic.AtomicInteger +import scala.scalajs.js + +class TracedHandlerSuite extends CatsEffectSuite { + import TracedHandlerSuite._ + + val fixture = ResourceFixture(TracesTestkit.inMemory[IO]()) + + fixture.test("single root span is created for single invocation") { traces => + traces.tracerProvider.tracer("test-tracer").get.flatMap { implicit tracer => + val allocationCounter = new AtomicInteger + val invokeCounter = new AtomicInteger + + val lambda = new IOLambda[TestEvent, String] { + def handler = + Resource.eval(IO(allocationCounter.getAndIncrement())).as { implicit inv => + def fn(ev: TestEvent): IO[Option[String]] = + for { + _ <- IO(invokeCounter.getAndIncrement()) + res = Some(ev.payload) + } yield res + + TracedHandler(fn) + } + } + + val event = TestEvent("1", "body") + + val functionName = "test-function-name" + val run = IO.fromPromise(IO(lambda.handlerFn(event.asJsAny, DummyContext(functionName)))) + + for { + res <- run + spans <- traces.finishedSpans + _ <- IO { + assertEquals(res, "body".toString.asInstanceOf[js.UndefOr[js.Any]]) + assertEquals(spans.length, 1) + assertEquals(spans.headOption.map(_.name), Some(functionName)) + } + } yield () + } + + } + + fixture.test("multiple root span per invocation created with function name ") { traces => + traces.tracerProvider.tracer("test-tracer").get.flatMap { implicit tracer => + val allocationCounter = new AtomicInteger + val invokeCounter = new AtomicInteger + + val lambda = new IOLambda[TestEvent, String] { + def handler = + Resource.eval(IO(allocationCounter.getAndIncrement())).as { implicit inv => + def fn(ev: TestEvent): IO[Option[String]] = + for { + _ <- IO(invokeCounter.getAndIncrement()) + res = Some(ev.payload) + } yield res + + TracedHandler(fn) + } + } + + val functionName = "test-function-name" + val chars = 'A'.to('C').toList + val run = + chars.zipWithIndex.map { case (c, i) => TestEvent(i.toString, c.toString) }.traverse { + e => IO.fromPromise(IO(lambda.handlerFn(e.asJsAny, DummyContext(functionName)))) + } + + val expectedSpanNames = List.fill(3)(functionName) + + for { + res <- run + spans <- traces.finishedSpans + _ <- IO { + assertEquals(res.length, chars.length) + assertEquals(spans.length, chars.length) + assertEquals(spans.map(_.name), expectedSpanNames) + } + } yield () + } + } + + object DummyContext { + def apply(fnName: String): facade.Context = new facade.Context { + def functionName = fnName + def functionVersion = "" + def invokedFunctionArn = "" + def memoryLimitInMB = "0" + def awsRequestId = "" + def logGroupName = "" + def logStreamName = "" + def identity = js.undefined + def clientContext = js.undefined + def getRemainingTimeInMillis(): Double = 0 + } + } + +} + +object TracedHandlerSuite { + + case class TestEvent(traceId: String, payload: String) + + object TestEvent { + + implicit val decoder: Decoder[TestEvent] = + Decoder.forProduct2("traceId", "payload")(TestEvent.apply) + implicit val encoder: Encoder[TestEvent] = + Encoder.forProduct2("traceId", "payload")(ev => (ev.traceId, ev.payload)) + + implicit val attr: EventSpanAttributes[TestEvent] = + new EventSpanAttributes[TestEvent] { + + override def contextCarrier(e: TestEvent): Map[String, String] = + Map("trace_id" -> e.traceId) + + override def spanKind: SpanKind = SpanKind.Consumer + + override def attributes(e: TestEvent): List[Attribute[_]] = List.empty + + } + } + + def tracedLambda(allocationCounter: AtomicInteger, invokeCounter: AtomicInteger) = {} + +} diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala index 6bc0d710..cca2659e 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala @@ -20,6 +20,8 @@ import cats.Monad import cats.syntax.all._ import feral.lambda.Invocation import org.typelevel.otel4s.trace.Tracer +import org.typelevel.otel4s.trace.SpanOps +import feral.lambda.Context object TracedHandler { @@ -33,19 +35,21 @@ object TracedHandler { event <- inv.event context <- inv.context res <- Tracer[F].joinOrRoot(attr.contextCarrier(event)) { - val spanR = - Tracer[F] - .spanBuilder(context.functionName) - .addAttributes(LambdaContextTraceAttributes(context)) - .withSpanKind(attr.spanKind) - .addAttributes(attr.attributes(event)) - .build - - spanR.surround { + buildSpan(event, context).surround { for { res <- handler(event) } yield res } } } yield res + + def buildSpan[F[_]: Tracer, Event](event: Event, context: Context[F])( + implicit attr: EventSpanAttributes[Event] + ): SpanOps[F] = + Tracer[F] + .spanBuilder(context.functionName) + .addAttributes(LambdaContextTraceAttributes(context)) + .withSpanKind(attr.spanKind) + .addAttributes(attr.attributes(event)) + .build } From 11be4712223966131610afafde99b94dd7558fac Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Tue, 27 Feb 2024 18:47:04 +0000 Subject: [PATCH 09/64] Rework package structure --- .../lambda/otel4s/EventAttributeSources.scala | 30 +++++++++++++++++++ .../lambda/otel4s/EventSpanAttributes.scala | 12 ++++++++ .../scala/feral/lambda/otel4s/package.scala | 20 ++----------- 3 files changed, 44 insertions(+), 18 deletions(-) create mode 100644 lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributeSources.scala diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributeSources.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributeSources.scala new file mode 100644 index 00000000..4d7ebfdb --- /dev/null +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributeSources.scala @@ -0,0 +1,30 @@ +package feral.lambda.otel4s + +import feral.lambda.events.SqsEvent +import org.typelevel.otel4s.Attribute +import org.typelevel.otel4s.trace.SpanKind +import feral.lambda.events.DynamoDbStreamEvent +import feral.lambda.events.ApiGatewayProxyEvent +import feral.lambda.events.ApiGatewayProxyEventV2 +import feral.lambda.events.S3BatchEvent + +protected[lambda] trait EventAttributeSources { + implicit def sqsEvent: EventSpanAttributes[SqsEvent] = + new EventSpanAttributes[SqsEvent] { + def contextCarrier(e: SqsEvent): Map[String, String] = + Map.empty + + def spanKind: SpanKind = SpanKind.Consumer + + def attributes(e: SqsEvent): List[Attribute[_]] = + SqsEventTraceAttributes() + } + + implicit def dynamoDbStreamEvent: EventSpanAttributes[DynamoDbStreamEvent] = ??? + + implicit def apiGatewayProxyEvent: EventSpanAttributes[ApiGatewayProxyEvent] = ??? + + implicit def apiGatewayProxyEventV2: EventSpanAttributes[ApiGatewayProxyEventV2] = ??? + + implicit def s3BatchEvent: EventSpanAttributes[S3BatchEvent] = ??? +} diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventSpanAttributes.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventSpanAttributes.scala index 9603cac2..73c5e261 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventSpanAttributes.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventSpanAttributes.scala @@ -19,8 +19,20 @@ package feral.lambda.otel4s import org.typelevel.otel4s.Attribute import org.typelevel.otel4s.trace.SpanKind +// TODO better name protected trait EventSpanAttributes[E] { def contextCarrier(e: E): Map[String, String] def spanKind: SpanKind def attributes(e: E): List[Attribute[_]] } + +object EventSpanAttributes { + def empty[E](sk: SpanKind): EventSpanAttributes[E] = + new EventSpanAttributes[E] { + def contextCarrier(e: E): Map[String, String] = + Map.empty + def spanKind: SpanKind = sk + def attributes(e: E): List[Attribute[_]] = + List.empty + } +} diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/package.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/package.scala index 84f226e8..da64ade6 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/package.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/package.scala @@ -1,3 +1,4 @@ +package feral.lambda /* * Copyright 2021 Typelevel * @@ -14,21 +15,4 @@ * limitations under the License. */ -package feral.lambda.otel4s - -import feral.lambda.events.SqsEvent -import org.typelevel.otel4s.Attribute -import org.typelevel.otel4s.trace.SpanKind - -package object implicits { - implicit def sqsEventSpanAttributes: EventSpanAttributes[SqsEvent] = - new EventSpanAttributes[SqsEvent] { - def contextCarrier(e: SqsEvent): Map[String, String] = - Map.empty - - def spanKind: SpanKind = SpanKind.Consumer - - def attributes(e: SqsEvent): List[Attribute[_]] = - SqsEventTraceAttributes() - } -} +package object otel4s extends EventAttributeSources From 08122de60fd17875e7d87f97fc723863405465b5 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Tue, 27 Feb 2024 18:53:52 +0000 Subject: [PATCH 10/64] prePR --- build.sbt | 2 +- .../scala/feral/examples/SqsOtelExample.scala | 2 +- .../lambda/otel4s/TracedHandlerSuite.scala | 16 ++++++++++++ .../lambda/otel4s/EventAttributeSources.scala | 26 +++++++++++++++---- .../feral/lambda/otel4s/TracedHandler.scala | 4 +-- .../scala/feral/lambda/otel4s/package.scala | 18 ++++++++++++- 6 files changed, 58 insertions(+), 10 deletions(-) diff --git a/build.sbt b/build.sbt index 929765ec..d351f0af 100644 --- a/build.sbt +++ b/build.sbt @@ -212,7 +212,7 @@ lazy val examples = crossProject(JSPlatform, JVMPlatform) "org.tpolecat" %%% "natchez-xray" % natchezVersion, "org.tpolecat" %%% "natchez-http4s" % "0.5.0", "org.tpolecat" %%% "skunk-core" % "0.6.3", - "org.typelevel" %%% "otel4s-sdk" % otel4sVersion, + "org.typelevel" %%% "otel4s-sdk" % otel4sVersion ) ) .settings(commonSettings) diff --git a/examples/src/main/scala/feral/examples/SqsOtelExample.scala b/examples/src/main/scala/feral/examples/SqsOtelExample.scala index 74b85d2c..3743c1ab 100644 --- a/examples/src/main/scala/feral/examples/SqsOtelExample.scala +++ b/examples/src/main/scala/feral/examples/SqsOtelExample.scala @@ -26,7 +26,7 @@ import feral.lambda.events.SqsEvent import feral.lambda.events.SqsRecord import feral.lambda.otel4s.SqsRecordTraceAttributes import feral.lambda.otel4s.TracedHandler -import feral.lambda.otel4s.implicits._ +import feral.lambda.otel4s._ import org.http4s.client.Client import org.http4s.ember.client.EmberClientBuilder import org.typelevel.otel4s.sdk.OpenTelemetrySdk diff --git a/lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala b/lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala index d2b8e087..11d77622 100644 --- a/lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala +++ b/lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2021 Typelevel + * + * Licensed 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 feral.lambda package otel4s diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributeSources.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributeSources.scala index 4d7ebfdb..0bdb9c1a 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributeSources.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributeSources.scala @@ -1,14 +1,30 @@ +/* + * Copyright 2021 Typelevel + * + * Licensed 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 feral.lambda.otel4s -import feral.lambda.events.SqsEvent -import org.typelevel.otel4s.Attribute -import org.typelevel.otel4s.trace.SpanKind -import feral.lambda.events.DynamoDbStreamEvent import feral.lambda.events.ApiGatewayProxyEvent import feral.lambda.events.ApiGatewayProxyEventV2 +import feral.lambda.events.DynamoDbStreamEvent import feral.lambda.events.S3BatchEvent +import feral.lambda.events.SqsEvent +import org.typelevel.otel4s.Attribute +import org.typelevel.otel4s.trace.SpanKind -protected[lambda] trait EventAttributeSources { +trait EventAttributeSources { implicit def sqsEvent: EventSpanAttributes[SqsEvent] = new EventSpanAttributes[SqsEvent] { def contextCarrier(e: SqsEvent): Map[String, String] = diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala index cca2659e..70b78a9b 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala @@ -18,10 +18,10 @@ package feral.lambda.otel4s import cats.Monad import cats.syntax.all._ +import feral.lambda.Context import feral.lambda.Invocation -import org.typelevel.otel4s.trace.Tracer import org.typelevel.otel4s.trace.SpanOps -import feral.lambda.Context +import org.typelevel.otel4s.trace.Tracer object TracedHandler { diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/package.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/package.scala index da64ade6..75e46bf3 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/package.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/package.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2021 Typelevel + * + * Licensed 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 feral.lambda /* * Copyright 2021 Typelevel @@ -15,4 +31,4 @@ package feral.lambda * limitations under the License. */ -package object otel4s extends EventAttributeSources +package object otel4s extends EventAttributeSources {} From 5bee0a35fc13fc20d06c360454c31bdb46be5750 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Tue, 12 Mar 2024 18:36:13 +0000 Subject: [PATCH 11/64] Swap tests from SDK to oteljava --- build.sbt | 7 +- .../scala/feral/examples/SqsOtelExample.scala | 142 +++++++-------- .../lambda/otel4s/TracedHandlerSuite.scala | 158 ---------------- .../lambda/otel4s/TracedHandlerSuite.scala | 172 ++++++++++++++++++ 4 files changed, 247 insertions(+), 232 deletions(-) delete mode 100644 lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala create mode 100644 lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala diff --git a/build.sbt b/build.sbt index d351f0af..d32e80ba 100644 --- a/build.sbt +++ b/build.sbt @@ -59,7 +59,7 @@ val natchezVersion = "0.3.5" val munitVersion = "0.7.29" val munitCEVersion = "1.0.7" val scalacheckEffectVersion = "1.0.4" -val otel4sVersion = "0.5-04ec3c4-20240223T163935Z-SNAPSHOT" +val otel4sVersion = "0.5.0-RC1" lazy val commonSettings = Seq( crossScalaVersions := Seq(Scala3, Scala213) @@ -194,12 +194,14 @@ lazy val lambdaOtel4s = crossProject(JSPlatform, JVMPlatform) libraryDependencies ++= Seq( "org.typelevel" %%% "otel4s-core-trace" % otel4sVersion, "org.typelevel" %%% "otel4s-semconv" % otel4sVersion, - "org.typelevel" %%% "otel4s-sdk-trace-testkit" % otel4sVersion % Test, "org.scalameta" %%% "munit-scalacheck" % munitVersion % Test, "org.typelevel" %%% "munit-cats-effect-3" % munitCEVersion % Test ) ) .settings(commonSettings) + .jvmSettings(libraryDependencies ++= Seq( + "org.typelevel" %%% "otel4s-oteljava-trace-testkit" % otel4sVersion % Test + )) .dependsOn(lambda) lazy val examples = crossProject(JSPlatform, JVMPlatform) @@ -212,7 +214,6 @@ lazy val examples = crossProject(JSPlatform, JVMPlatform) "org.tpolecat" %%% "natchez-xray" % natchezVersion, "org.tpolecat" %%% "natchez-http4s" % "0.5.0", "org.tpolecat" %%% "skunk-core" % "0.6.3", - "org.typelevel" %%% "otel4s-sdk" % otel4sVersion ) ) .settings(commonSettings) diff --git a/examples/src/main/scala/feral/examples/SqsOtelExample.scala b/examples/src/main/scala/feral/examples/SqsOtelExample.scala index 3743c1ab..f41c1189 100644 --- a/examples/src/main/scala/feral/examples/SqsOtelExample.scala +++ b/examples/src/main/scala/feral/examples/SqsOtelExample.scala @@ -1,79 +1,79 @@ -/* - * Copyright 2021 Typelevel - * - * Licensed 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. - */ +// /* +// * Copyright 2021 Typelevel +// * +// * Licensed 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 feral.examples +// package feral.examples -import cats.Monad -import cats.effect.IO -import cats.syntax.all._ -import feral.lambda.INothing -import feral.lambda.IOLambda -import feral.lambda.Invocation -import feral.lambda.events.SqsEvent -import feral.lambda.events.SqsRecord -import feral.lambda.otel4s.SqsRecordTraceAttributes -import feral.lambda.otel4s.TracedHandler -import feral.lambda.otel4s._ -import org.http4s.client.Client -import org.http4s.ember.client.EmberClientBuilder -import org.typelevel.otel4s.sdk.OpenTelemetrySdk -import org.typelevel.otel4s.trace.Tracer -import org.typelevel.scalaccompat.annotation.unused +// import cats.Monad +// import cats.effect.IO +// import cats.syntax.all._ +// import feral.lambda.INothing +// import feral.lambda.IOLambda +// import feral.lambda.Invocation +// import feral.lambda.events.SqsEvent +// import feral.lambda.events.SqsRecord +// import feral.lambda.otel4s.SqsRecordTraceAttributes +// import feral.lambda.otel4s.TracedHandler +// import feral.lambda.otel4s._ +// import org.http4s.client.Client +// import org.http4s.ember.client.EmberClientBuilder +// import org.typelevel.otel4s.sdk.OpenTelemetrySdk +// import org.typelevel.otel4s.trace.Tracer +// import org.typelevel.scalaccompat.annotation.unused -object SqsOtelExample extends IOLambda[SqsEvent, INothing] { +// object SqsOtelExample extends IOLambda[SqsEvent, INothing] { - def handler = { - OpenTelemetrySdk - .autoConfigured[IO]() - .map(_.sdk) - .evalMap(_.tracerProvider.get("feral.examples.SqsOtelExample")) - .flatMap { implicit tracer: Tracer[IO] => - for { - client <- EmberClientBuilder.default[IO].build - tracedClient = clientTraceMiddleware(client) - } yield { implicit inv: Invocation[IO, SqsEvent] => - TracedHandler[IO, SqsEvent, INothing]( - handleEvent[IO](tracedClient) - ) - } - } - } +// def handler = { +// OpenTelemetrySdk +// .autoConfigured[IO]() +// .map(_.sdk) +// .evalMap(_.tracerProvider.get("feral.examples.SqsOtelExample")) +// .flatMap { implicit tracer: Tracer[IO] => +// for { +// client <- EmberClientBuilder.default[IO].build +// tracedClient = clientTraceMiddleware(client) +// } yield { implicit inv: Invocation[IO, SqsEvent] => +// TracedHandler[IO, SqsEvent, INothing]( +// handleEvent[IO](tracedClient) +// ) +// } +// } +// } - def handleEvent[F[_]: Monad: Tracer]( - @unused client: Client[F] - ): SqsEvent => F[Option[INothing]] = { event => - event - .records - .traverse(record => - Tracer[F].span("handle-record", SqsRecordTraceAttributes(record)).surround { - handleRecord[F](record) - }) - .as(None) - } +// def handleEvent[F[_]: Monad: Tracer]( +// @unused client: Client[F] +// ): SqsEvent => F[Option[INothing]] = { event => +// event +// .records +// .traverse(record => +// Tracer[F].span("handle-record", SqsRecordTraceAttributes(record)).surround { +// handleRecord[F](record) +// }) +// .as(None) +// } - def handleRecord[F[_]: Monad: Tracer](@unused record: SqsRecord): F[Unit] = { - Tracer[F].span("some-operation").surround { - Monad[F].unit - } - } +// def handleRecord[F[_]: Monad: Tracer](@unused record: SqsRecord): F[Unit] = { +// Tracer[F].span("some-operation").surround { +// Monad[F].unit +// } +// } - // stub client middleware while http4s-otel-middleware catches up to otel4s - // 0.5 - private def clientTraceMiddleware[F[_]](client: Client[F])(implicit @unused T: Tracer[IO]) = { - client - } +// // stub client middleware while http4s-otel-middleware catches up to otel4s +// // 0.5 +// private def clientTraceMiddleware[F[_]](client: Client[F])(implicit @unused T: Tracer[IO]) = { +// client +// } -} +// } diff --git a/lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala b/lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala deleted file mode 100644 index 11d77622..00000000 --- a/lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright 2021 Typelevel - * - * Licensed 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 feral.lambda -package otel4s - -import cats.effect.IO -import cats.effect.kernel.Resource -import cats.syntax.all._ -import feral.lambda.IOLambda -import io.circe.Decoder -import io.circe.Encoder -import io.circe.scalajs._ -import munit.CatsEffectSuite -import org.typelevel.otel4s.Attribute -import org.typelevel.otel4s.sdk.testkit.trace.TracesTestkit -import org.typelevel.otel4s.trace.SpanKind - -import java.util.concurrent.atomic.AtomicInteger -import scala.scalajs.js - -class TracedHandlerSuite extends CatsEffectSuite { - import TracedHandlerSuite._ - - val fixture = ResourceFixture(TracesTestkit.inMemory[IO]()) - - fixture.test("single root span is created for single invocation") { traces => - traces.tracerProvider.tracer("test-tracer").get.flatMap { implicit tracer => - val allocationCounter = new AtomicInteger - val invokeCounter = new AtomicInteger - - val lambda = new IOLambda[TestEvent, String] { - def handler = - Resource.eval(IO(allocationCounter.getAndIncrement())).as { implicit inv => - def fn(ev: TestEvent): IO[Option[String]] = - for { - _ <- IO(invokeCounter.getAndIncrement()) - res = Some(ev.payload) - } yield res - - TracedHandler(fn) - } - } - - val event = TestEvent("1", "body") - - val functionName = "test-function-name" - val run = IO.fromPromise(IO(lambda.handlerFn(event.asJsAny, DummyContext(functionName)))) - - for { - res <- run - spans <- traces.finishedSpans - _ <- IO { - assertEquals(res, "body".toString.asInstanceOf[js.UndefOr[js.Any]]) - assertEquals(spans.length, 1) - assertEquals(spans.headOption.map(_.name), Some(functionName)) - } - } yield () - } - - } - - fixture.test("multiple root span per invocation created with function name ") { traces => - traces.tracerProvider.tracer("test-tracer").get.flatMap { implicit tracer => - val allocationCounter = new AtomicInteger - val invokeCounter = new AtomicInteger - - val lambda = new IOLambda[TestEvent, String] { - def handler = - Resource.eval(IO(allocationCounter.getAndIncrement())).as { implicit inv => - def fn(ev: TestEvent): IO[Option[String]] = - for { - _ <- IO(invokeCounter.getAndIncrement()) - res = Some(ev.payload) - } yield res - - TracedHandler(fn) - } - } - - val functionName = "test-function-name" - val chars = 'A'.to('C').toList - val run = - chars.zipWithIndex.map { case (c, i) => TestEvent(i.toString, c.toString) }.traverse { - e => IO.fromPromise(IO(lambda.handlerFn(e.asJsAny, DummyContext(functionName)))) - } - - val expectedSpanNames = List.fill(3)(functionName) - - for { - res <- run - spans <- traces.finishedSpans - _ <- IO { - assertEquals(res.length, chars.length) - assertEquals(spans.length, chars.length) - assertEquals(spans.map(_.name), expectedSpanNames) - } - } yield () - } - } - - object DummyContext { - def apply(fnName: String): facade.Context = new facade.Context { - def functionName = fnName - def functionVersion = "" - def invokedFunctionArn = "" - def memoryLimitInMB = "0" - def awsRequestId = "" - def logGroupName = "" - def logStreamName = "" - def identity = js.undefined - def clientContext = js.undefined - def getRemainingTimeInMillis(): Double = 0 - } - } - -} - -object TracedHandlerSuite { - - case class TestEvent(traceId: String, payload: String) - - object TestEvent { - - implicit val decoder: Decoder[TestEvent] = - Decoder.forProduct2("traceId", "payload")(TestEvent.apply) - implicit val encoder: Encoder[TestEvent] = - Encoder.forProduct2("traceId", "payload")(ev => (ev.traceId, ev.payload)) - - implicit val attr: EventSpanAttributes[TestEvent] = - new EventSpanAttributes[TestEvent] { - - override def contextCarrier(e: TestEvent): Map[String, String] = - Map("trace_id" -> e.traceId) - - override def spanKind: SpanKind = SpanKind.Consumer - - override def attributes(e: TestEvent): List[Attribute[_]] = List.empty - - } - } - - def tracedLambda(allocationCounter: AtomicInteger, invokeCounter: AtomicInteger) = {} - -} diff --git a/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala b/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala new file mode 100644 index 00000000..b0bb9c71 --- /dev/null +++ b/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala @@ -0,0 +1,172 @@ +/* + * Copyright 2021 Typelevel + * + * Licensed 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 feral.lambda +package otel4s + +import io.circe.syntax._ +import cats.effect.IO +import com.amazonaws.services.lambda.runtime +import cats.effect.kernel.Resource +import cats.syntax.all._ +import feral.lambda.IOLambda +import io.circe.Decoder +import io.circe.Encoder +import munit.CatsEffectSuite +import org.typelevel.otel4s.Attribute +import org.typelevel.otel4s.oteljava.testkit.trace.TracesTestkit +import org.typelevel.otel4s.trace.SpanKind + +import java.util.concurrent.atomic.AtomicInteger +import java.io.ByteArrayOutputStream +import java.io.ByteArrayInputStream + +class TracedHandlerSuite extends CatsEffectSuite { + import TracedHandlerSuite._ + + implicit class HandleOps[A, B](lambda: IOLambda[A, B]) { + def handleRequestHelper(in: String, ctx: runtime.Context): String = { + val os = new ByteArrayOutputStream + lambda.handleRequest( + new ByteArrayInputStream(in.getBytes()), + os, + ctx + ) + new String(os.toByteArray()) + } + } + + val fixture = ResourceFixture(TracesTestkit.inMemory[IO]()) + + fixture.test("single root span is created for single invocation") { traces => + traces.tracerProvider.tracer("test-tracer").get.flatMap { implicit tracer => + val allocationCounter = new AtomicInteger + val invokeCounter = new AtomicInteger + + val lambda = new IOLambda[TestEvent, String] { + def handler = + Resource.eval(IO(allocationCounter.getAndIncrement())).as { implicit inv => + def fn(ev: TestEvent): IO[Option[String]] = + for { + _ <- IO(invokeCounter.getAndIncrement()) + res = Some(ev.payload) + } yield res + + TracedHandler(fn) + } + } + + val event = TestEvent("1", "body") + val payload = event.asJson.noSpaces + + val functionName = "test-function-name" + val run = IO(lambda.handleRequestHelper(payload, DummyContext(functionName))) + + for { + res <- run + spans <- traces.finishedSpans + _ <- IO { + assertEquals(res, "\"body\"".toString) + assertEquals(spans.length, 1) + // assertEquals(spans.headOption.map(_.name), Some(functionName)) + } + } yield () + } + + } + + // fixture.test("multiple root span per invocation created with function name ") { traces => + // traces.tracerProvider.tracer("test-tracer").get.flatMap { implicit tracer => + // val allocationCounter = new AtomicInteger + // val invokeCounter = new AtomicInteger + + // val lambda = new IOLambda[TestEvent, String] { + // def handler = + // Resource.eval(IO(allocationCounter.getAndIncrement())).as { implicit inv => + // def fn(ev: TestEvent): IO[Option[String]] = + // for { + // _ <- IO(invokeCounter.getAndIncrement()) + // res = Some(ev.payload) + // } yield res + + // TracedHandler(fn) + // } + // } + + // val functionName = "test-function-name" + // val chars = 'A'.to('C').toList + // val run = + // chars.zipWithIndex.map { case (c, i) => TestEvent(i.toString, c.toString) }.traverse { + // e => IO.fromPromise(IO(lambda.handlerFn(e.asJsAny, DummyContext(functionName)))) + // } + + // val expectedSpanNames = List.fill(3)(functionName) + + // for { + // res <- run + // spans <- traces.finishedSpans + // _ <- IO { + // assertEquals(res.length, chars.length) + // assertEquals(spans.length, chars.length) + // assertEquals(spans.map(_.name), expectedSpanNames) + // } + // } yield () + // } + // } + + case class DummyContext(functionName: String) extends runtime.Context { + override def getAwsRequestId(): String = "" + override def getLogGroupName(): String = "" + override def getLogStreamName(): String = "" + override def getFunctionName(): String = functionName + override def getFunctionVersion(): String = "" + override def getInvokedFunctionArn(): String = "" + override def getIdentity(): runtime.CognitoIdentity = null + override def getClientContext(): runtime.ClientContext = null + override def getRemainingTimeInMillis(): Int = Int.MaxValue + override def getMemoryLimitInMB(): Int = 0 + override def getLogger(): runtime.LambdaLogger = null + } + +} + +object TracedHandlerSuite { + + case class TestEvent(traceId: String, payload: String) + + object TestEvent { + + implicit val decoder: Decoder[TestEvent] = + Decoder.forProduct2("traceId", "payload")(TestEvent.apply) + implicit val encoder: Encoder[TestEvent] = + Encoder.forProduct2("traceId", "payload")(ev => (ev.traceId, ev.payload)) + + implicit val attr: EventSpanAttributes[TestEvent] = + new EventSpanAttributes[TestEvent] { + + override def contextCarrier(e: TestEvent): Map[String, String] = + Map("trace_id" -> e.traceId) + + override def spanKind: SpanKind = SpanKind.Consumer + + override def attributes(e: TestEvent): List[Attribute[_]] = List.empty + + } + } + + def tracedLambda(allocationCounter: AtomicInteger, invokeCounter: AtomicInteger) = {} + +} From 6b8015062bcd619124294d854da37d5e06871270 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Tue, 12 Mar 2024 18:47:42 +0000 Subject: [PATCH 12/64] Run prePR --- build.sbt | 2 +- .../scala/feral/examples/SqsOtelExample.scala | 16 ++++++++++++++++ .../feral/lambda/otel4s/TracedHandlerSuite.scala | 9 ++++----- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/build.sbt b/build.sbt index d32e80ba..97f4f14c 100644 --- a/build.sbt +++ b/build.sbt @@ -213,7 +213,7 @@ lazy val examples = crossProject(JSPlatform, JVMPlatform) "org.http4s" %%% "http4s-ember-client" % http4sVersion, "org.tpolecat" %%% "natchez-xray" % natchezVersion, "org.tpolecat" %%% "natchez-http4s" % "0.5.0", - "org.tpolecat" %%% "skunk-core" % "0.6.3", + "org.tpolecat" %%% "skunk-core" % "0.6.3" ) ) .settings(commonSettings) diff --git a/examples/src/main/scala/feral/examples/SqsOtelExample.scala b/examples/src/main/scala/feral/examples/SqsOtelExample.scala index f41c1189..005e1b4b 100644 --- a/examples/src/main/scala/feral/examples/SqsOtelExample.scala +++ b/examples/src/main/scala/feral/examples/SqsOtelExample.scala @@ -1,3 +1,19 @@ +/* + * Copyright 2021 Typelevel + * + * Licensed 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. + */ + // /* // * Copyright 2021 Typelevel // * diff --git a/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala b/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala index b0bb9c71..f32e9eea 100644 --- a/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala +++ b/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala @@ -17,22 +17,21 @@ package feral.lambda package otel4s -import io.circe.syntax._ import cats.effect.IO -import com.amazonaws.services.lambda.runtime import cats.effect.kernel.Resource import cats.syntax.all._ -import feral.lambda.IOLambda +import com.amazonaws.services.lambda.runtime import io.circe.Decoder import io.circe.Encoder +import io.circe.syntax._ import munit.CatsEffectSuite import org.typelevel.otel4s.Attribute import org.typelevel.otel4s.oteljava.testkit.trace.TracesTestkit import org.typelevel.otel4s.trace.SpanKind -import java.util.concurrent.atomic.AtomicInteger -import java.io.ByteArrayOutputStream import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.util.concurrent.atomic.AtomicInteger class TracedHandlerSuite extends CatsEffectSuite { import TracedHandlerSuite._ From 5489668945ed1ca39ca3ce3691475bf97134869b Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Tue, 12 Mar 2024 19:02:46 +0000 Subject: [PATCH 13/64] Add back extra JVM test --- .../lambda/otel4s/TracedHandlerSuite.scala | 80 ++++++++++--------- 1 file changed, 41 insertions(+), 39 deletions(-) diff --git a/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala b/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala index f32e9eea..34921fc4 100644 --- a/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala +++ b/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala @@ -80,51 +80,53 @@ class TracedHandlerSuite extends CatsEffectSuite { _ <- IO { assertEquals(res, "\"body\"".toString) assertEquals(spans.length, 1) - // assertEquals(spans.headOption.map(_.name), Some(functionName)) + assertEquals(spans.headOption.map(_.getName()), Some(functionName)) } } yield () } } - // fixture.test("multiple root span per invocation created with function name ") { traces => - // traces.tracerProvider.tracer("test-tracer").get.flatMap { implicit tracer => - // val allocationCounter = new AtomicInteger - // val invokeCounter = new AtomicInteger - - // val lambda = new IOLambda[TestEvent, String] { - // def handler = - // Resource.eval(IO(allocationCounter.getAndIncrement())).as { implicit inv => - // def fn(ev: TestEvent): IO[Option[String]] = - // for { - // _ <- IO(invokeCounter.getAndIncrement()) - // res = Some(ev.payload) - // } yield res - - // TracedHandler(fn) - // } - // } - - // val functionName = "test-function-name" - // val chars = 'A'.to('C').toList - // val run = - // chars.zipWithIndex.map { case (c, i) => TestEvent(i.toString, c.toString) }.traverse { - // e => IO.fromPromise(IO(lambda.handlerFn(e.asJsAny, DummyContext(functionName)))) - // } - - // val expectedSpanNames = List.fill(3)(functionName) - - // for { - // res <- run - // spans <- traces.finishedSpans - // _ <- IO { - // assertEquals(res.length, chars.length) - // assertEquals(spans.length, chars.length) - // assertEquals(spans.map(_.name), expectedSpanNames) - // } - // } yield () - // } - // } + fixture.test("multiple root span per invocation created with function name ") { traces => + traces.tracerProvider.tracer("test-tracer").get.flatMap { implicit tracer => + val allocationCounter = new AtomicInteger + val invokeCounter = new AtomicInteger + + val lambda = new IOLambda[TestEvent, String] { + def handler = + Resource.eval(IO(allocationCounter.getAndIncrement())).as { implicit inv => + def fn(ev: TestEvent): IO[Option[String]] = + for { + _ <- IO(invokeCounter.getAndIncrement()) + res = Some(ev.payload) + } yield res + + TracedHandler(fn) + } + } + + val functionName = "test-function-name" + val chars = 'A'.to('C').toList + val run = + chars.zipWithIndex.map { case (c, i) => TestEvent(i.toString, c.toString) }.traverse { + e => + val payload = e.asJson.noSpaces + IO(lambda.handleRequestHelper(payload, DummyContext(functionName))) + } + + val expectedSpanNames = List.fill(3)(functionName) + + for { + res <- run + spans <- traces.finishedSpans + _ <- IO { + assertEquals(res.length, chars.length) + assertEquals(spans.length, chars.length) + assertEquals(spans.map(_.getName()), expectedSpanNames) + } + } yield () + } + } case class DummyContext(functionName: String) extends runtime.Context { override def getAwsRequestId(): String = "" From 4ae91a8e8beb6bb53cfbc90b711e49b05f00c585 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Tue, 12 Mar 2024 19:20:19 +0000 Subject: [PATCH 14/64] Add OtelJava JVM example --- build.sbt | 6 +- .../feral/examples/SqsOtelJavaExample.scala | 75 +++++++++++++++ .../scala/feral/examples/Http4sLambda.scala | 8 +- .../scala/feral/examples/KinesisLambda.scala | 4 +- .../scala/feral/examples/SqsOtelExample.scala | 95 ------------------- 5 files changed, 86 insertions(+), 102 deletions(-) create mode 100644 examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala rename examples/{ => shared}/src/main/scala/feral/examples/Http4sLambda.scala (95%) rename examples/{ => shared}/src/main/scala/feral/examples/KinesisLambda.scala (97%) delete mode 100644 examples/src/main/scala/feral/examples/SqsOtelExample.scala diff --git a/build.sbt b/build.sbt index 97f4f14c..22bbc936 100644 --- a/build.sbt +++ b/build.sbt @@ -205,7 +205,6 @@ lazy val lambdaOtel4s = crossProject(JSPlatform, JVMPlatform) .dependsOn(lambda) lazy val examples = crossProject(JSPlatform, JVMPlatform) - .crossType(CrossType.Pure) .in(file("examples")) .settings( libraryDependencies ++= Seq( @@ -217,6 +216,11 @@ lazy val examples = crossProject(JSPlatform, JVMPlatform) ) ) .settings(commonSettings) + .jvmSettings( + libraryDependencies ++= Seq( + "org.typelevel" %%% "otel4s-oteljava" % otel4sVersion, + "io.opentelemetry" % "opentelemetry-sdk-extension-autoconfigure" % "1.34.1" + )) .dependsOn(lambda, lambdaNatchez, lambdaHttp4s, lambdaOtel4s) .enablePlugins(NoPublishPlugin) diff --git a/examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala b/examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala new file mode 100644 index 00000000..300bbb69 --- /dev/null +++ b/examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala @@ -0,0 +1,75 @@ +/* + * Copyright 2021 Typelevel + * + * Licensed 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 feral.examples + +import _root_.feral.lambda.INothing +import _root_.feral.lambda.IOLambda +import _root_.feral.lambda.Invocation +import _root_.feral.lambda.events.SqsEvent +import _root_.feral.lambda.events.SqsRecord +import _root_.feral.lambda.otel4s.SqsRecordTraceAttributes +import _root_.feral.lambda.otel4s.TracedHandler +import _root_.feral.lambda.otel4s._ +import cats.Monad +import cats.effect.IO +import cats.syntax.all._ +import org.http4s.client.Client +import org.http4s.ember.client.EmberClientBuilder +import org.typelevel.otel4s.oteljava.OtelJava +import org.typelevel.otel4s.trace.Tracer +import org.typelevel.scalaccompat.annotation.unused + +object SqsOtelExample extends IOLambda[SqsEvent, INothing] { + + def handler = + OtelJava.autoConfigured[IO]().map(_.tracerProvider).evalMap(_.get("tracer")).flatMap { + implicit tracer: Tracer[IO] => + for { + client <- EmberClientBuilder.default[IO].build + tracedClient = clientTraceMiddleware(client) + } yield { implicit inv: Invocation[IO, SqsEvent] => + TracedHandler[IO, SqsEvent, INothing]( + handleEvent[IO](tracedClient) + ) + } + } + + def handleEvent[F[_]: Monad: Tracer]( + @unused client: Client[F] + ): SqsEvent => F[Option[INothing]] = { event => + event + .records + .traverse(record => + Tracer[F].span("handle-record", SqsRecordTraceAttributes(record)).surround { + handleRecord[F](record) + }) + .as(None) + } + + def handleRecord[F[_]: Monad: Tracer](@unused record: SqsRecord): F[Unit] = { + Tracer[F].span("some-operation").surround { + Monad[F].unit + } + } + + // stub client middleware while http4s-otel-middleware catches up to otel4s + // 0.5 + private def clientTraceMiddleware[F[_]](client: Client[F])(implicit @unused T: Tracer[IO]) = { + client + } + +} diff --git a/examples/src/main/scala/feral/examples/Http4sLambda.scala b/examples/shared/src/main/scala/feral/examples/Http4sLambda.scala similarity index 95% rename from examples/src/main/scala/feral/examples/Http4sLambda.scala rename to examples/shared/src/main/scala/feral/examples/Http4sLambda.scala index bf55ff48..3a78ee4f 100644 --- a/examples/src/main/scala/feral/examples/Http4sLambda.scala +++ b/examples/shared/src/main/scala/feral/examples/Http4sLambda.scala @@ -16,15 +16,15 @@ package feral.examples +import _root_.feral.lambda._ +import _root_.feral.lambda.events._ +import _root_.feral.lambda.http4s._ +import _root_.feral.lambda.natchez._ import _root_.natchez.Trace import _root_.natchez.http4s.NatchezMiddleware import _root_.natchez.xray.XRay import cats.effect._ import cats.effect.std.Random -import feral.lambda._ -import feral.lambda.events._ -import feral.lambda.http4s._ -import feral.lambda.natchez._ import org.http4s.HttpApp import org.http4s.HttpRoutes import org.http4s.client.Client diff --git a/examples/src/main/scala/feral/examples/KinesisLambda.scala b/examples/shared/src/main/scala/feral/examples/KinesisLambda.scala similarity index 97% rename from examples/src/main/scala/feral/examples/KinesisLambda.scala rename to examples/shared/src/main/scala/feral/examples/KinesisLambda.scala index 06599c5c..8b6a2baf 100644 --- a/examples/src/main/scala/feral/examples/KinesisLambda.scala +++ b/examples/shared/src/main/scala/feral/examples/KinesisLambda.scala @@ -16,12 +16,12 @@ package feral.examples +import _root_.feral.lambda._ +import _root_.feral.lambda.events.SqsEvent import _root_.natchez.Trace import _root_.natchez.xray.XRay import cats.effect._ import cats.effect.std.Random -import feral.lambda._ -import feral.lambda.events.SqsEvent import skunk.Session /** diff --git a/examples/src/main/scala/feral/examples/SqsOtelExample.scala b/examples/src/main/scala/feral/examples/SqsOtelExample.scala deleted file mode 100644 index 005e1b4b..00000000 --- a/examples/src/main/scala/feral/examples/SqsOtelExample.scala +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2021 Typelevel - * - * Licensed 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. - */ - -// /* -// * Copyright 2021 Typelevel -// * -// * Licensed 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 feral.examples - -// import cats.Monad -// import cats.effect.IO -// import cats.syntax.all._ -// import feral.lambda.INothing -// import feral.lambda.IOLambda -// import feral.lambda.Invocation -// import feral.lambda.events.SqsEvent -// import feral.lambda.events.SqsRecord -// import feral.lambda.otel4s.SqsRecordTraceAttributes -// import feral.lambda.otel4s.TracedHandler -// import feral.lambda.otel4s._ -// import org.http4s.client.Client -// import org.http4s.ember.client.EmberClientBuilder -// import org.typelevel.otel4s.sdk.OpenTelemetrySdk -// import org.typelevel.otel4s.trace.Tracer -// import org.typelevel.scalaccompat.annotation.unused - -// object SqsOtelExample extends IOLambda[SqsEvent, INothing] { - -// def handler = { -// OpenTelemetrySdk -// .autoConfigured[IO]() -// .map(_.sdk) -// .evalMap(_.tracerProvider.get("feral.examples.SqsOtelExample")) -// .flatMap { implicit tracer: Tracer[IO] => -// for { -// client <- EmberClientBuilder.default[IO].build -// tracedClient = clientTraceMiddleware(client) -// } yield { implicit inv: Invocation[IO, SqsEvent] => -// TracedHandler[IO, SqsEvent, INothing]( -// handleEvent[IO](tracedClient) -// ) -// } -// } -// } - -// def handleEvent[F[_]: Monad: Tracer]( -// @unused client: Client[F] -// ): SqsEvent => F[Option[INothing]] = { event => -// event -// .records -// .traverse(record => -// Tracer[F].span("handle-record", SqsRecordTraceAttributes(record)).surround { -// handleRecord[F](record) -// }) -// .as(None) -// } - -// def handleRecord[F[_]: Monad: Tracer](@unused record: SqsRecord): F[Unit] = { -// Tracer[F].span("some-operation").surround { -// Monad[F].unit -// } -// } - -// // stub client middleware while http4s-otel-middleware catches up to otel4s -// // 0.5 -// private def clientTraceMiddleware[F[_]](client: Client[F])(implicit @unused T: Tracer[IO]) = { -// client -// } - -// } From 29f82486ed582e758aeb72d07f09b1bde9705838 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Thu, 18 Apr 2024 21:50:09 +0100 Subject: [PATCH 15/64] Update otel4s to 0.6.0 --- build.sbt | 3 ++- .../feral/lambda/otel4s/EventAttributes.scala | 13 ++++------- .../feral/lambda/otel4s/FaasAttributes.scala | 22 +++++++++---------- 3 files changed, 17 insertions(+), 21 deletions(-) diff --git a/build.sbt b/build.sbt index 22bbc936..d77a4525 100644 --- a/build.sbt +++ b/build.sbt @@ -59,7 +59,7 @@ val natchezVersion = "0.3.5" val munitVersion = "0.7.29" val munitCEVersion = "1.0.7" val scalacheckEffectVersion = "1.0.4" -val otel4sVersion = "0.5.0-RC1" +val otel4sVersion = "0.6.0" lazy val commonSettings = Seq( crossScalaVersions := Seq(Scala3, Scala213) @@ -194,6 +194,7 @@ lazy val lambdaOtel4s = crossProject(JSPlatform, JVMPlatform) libraryDependencies ++= Seq( "org.typelevel" %%% "otel4s-core-trace" % otel4sVersion, "org.typelevel" %%% "otel4s-semconv" % otel4sVersion, + "org.typelevel" %%% "otel4s-semconv-experimental" % otel4sVersion, "org.scalameta" %%% "munit-scalacheck" % munitVersion % Test, "org.typelevel" %%% "munit-cats-effect-3" % munitCEVersion % Test ) diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala index ea8345c1..53e6495f 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala @@ -18,28 +18,23 @@ package feral.lambda.otel4s import feral.lambda.events.SqsRecord import org.typelevel.otel4s.Attribute -import org.typelevel.otel4s.semconv.trace.attributes.SemanticAttributes +import org.typelevel.otel4s.semconv.experimental.attributes.FaasExperimentalAttributes._ +import org.typelevel.otel4s.semconv.experimental.attributes.MessagingExperimentalAttributes._ object SqsEventTraceAttributes { - def apply(): List[Attribute[_]] = { - import SemanticAttributes._ - + def apply(): List[Attribute[_]] = List( FaasTrigger(FaasTriggerValue.Pubsub.value), MessagingSystem("aws_sqs") ) - } } object SqsRecordTraceAttributes { - def apply(e: SqsRecord): List[Attribute[_]] = { - import SemanticAttributes._ - + def apply(e: SqsRecord): List[Attribute[_]] = List( FaasTrigger(FaasTriggerValue.Pubsub.value), MessagingOperation(MessagingOperationValue.Receive.value), MessagingMessageId(e.messageId), MessagingDestinationName(e.eventSource) ) - } } diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/FaasAttributes.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/FaasAttributes.scala index 060a0e6a..eed50f36 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/FaasAttributes.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/FaasAttributes.scala @@ -18,24 +18,24 @@ package feral.lambda.otel4s import feral.lambda.Context import org.typelevel.otel4s.Attribute -import org.typelevel.otel4s.semconv.resource.attributes.ResourceAttributes -import org.typelevel.otel4s.semconv.trace.attributes.SemanticAttributes +import org.typelevel.otel4s.semconv.experimental.attributes.CloudExperimentalAttributes +import org.typelevel.otel4s.semconv.experimental.attributes.FaasExperimentalAttributes object LambdaContextTraceAttributes { - val InvocationId = SemanticAttributes.FaasInvocationId - val FaasTrigger = SemanticAttributes.FaasTrigger + val InvocationId = FaasExperimentalAttributes.FaasInvocationId + val FaasTrigger = FaasExperimentalAttributes.FaasTrigger // ARN - val CloudResourceId = ResourceAttributes.CloudResourceId + val CloudResourceId = CloudExperimentalAttributes.CloudResourceId // log stream name - val FaasInstance = ResourceAttributes.FaasInstance - val FaasMaxMemory = ResourceAttributes.FaasMaxMemory - val FaasName = ResourceAttributes.FaasName - val FaasVersion = ResourceAttributes.FaasVersion - val CloudProvider = ResourceAttributes.CloudProvider + val FaasInstance = FaasExperimentalAttributes.FaasInstance + val FaasMaxMemory = FaasExperimentalAttributes.FaasMaxMemory + val FaasName = FaasExperimentalAttributes.FaasName + val FaasVersion = FaasExperimentalAttributes.FaasVersion + val CloudProvider = CloudExperimentalAttributes.CloudProvider def apply[F[_]](context: Context[F]): List[Attribute[_]] = { List( - CloudProvider(ResourceAttributes.CloudProviderValue.Aws.value), + CloudProvider(CloudExperimentalAttributes.CloudProviderValue.Aws.value), CloudResourceId(context.invokedFunctionArn), FaasInstance(context.logStreamName), FaasMaxMemory(context.memoryLimitInMB.toLong), From db566205414e8429485907ae1b9091fb767643f0 Mon Sep 17 00:00:00 2001 From: Alex Cardell <29524087+alexcardell@users.noreply.github.com> Date: Thu, 18 Apr 2024 21:52:30 +0100 Subject: [PATCH 16/64] Simplify for-comp Co-authored-by: Maksym Ochenashko --- .../src/main/scala/feral/lambda/otel4s/TracedHandler.scala | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala index 70b78a9b..72095d26 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala @@ -36,9 +36,7 @@ object TracedHandler { context <- inv.context res <- Tracer[F].joinOrRoot(attr.contextCarrier(event)) { buildSpan(event, context).surround { - for { - res <- handler(event) - } yield res + handler(event) } } } yield res From a73765448454b88143f66de98476f1b0c89e0486 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Thu, 18 Apr 2024 21:54:47 +0100 Subject: [PATCH 17/64] Fix headers --- .../main/scala/feral/lambda/otel4s/package.scala | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/package.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/package.scala index 75e46bf3..9d016a73 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/package.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/package.scala @@ -15,20 +15,5 @@ */ package feral.lambda -/* - * Copyright 2021 Typelevel - * - * Licensed 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 object otel4s extends EventAttributeSources {} From 5d0fb2b8b47b3c7d9090d6bf6ab7e25edb641d72 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Thu, 18 Apr 2024 22:02:51 +0100 Subject: [PATCH 18/64] Update scalaJS plugin --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 106a4b3d..abc884f6 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,5 +1,5 @@ val sbtlTlV = "0.6.7" addSbtPlugin("org.typelevel" % "sbt-typelevel" % sbtlTlV) addSbtPlugin("org.typelevel" % "sbt-typelevel-scalafix" % sbtlTlV) -addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.15.0") +addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.16.0") addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.12.0") From 9abdd5ecb642a8b81280f761367ca4a5f136e90d Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Fri, 19 Apr 2024 18:17:22 +0100 Subject: [PATCH 19/64] Remove semconv-experimental --- build.sbt | 2 - .../feral/lambda/otel4s/EventAttributes.scala | 5 +- .../feral/lambda/otel4s/FaasAttributes.scala | 57 ++++++++++++++----- .../feral/lambda/otel4s/TracedHandler.scala | 2 +- 4 files changed, 48 insertions(+), 18 deletions(-) diff --git a/build.sbt b/build.sbt index d77a4525..4b9f7bc6 100644 --- a/build.sbt +++ b/build.sbt @@ -193,8 +193,6 @@ lazy val lambdaOtel4s = crossProject(JSPlatform, JVMPlatform) name := "feral-lambda-otel4s", libraryDependencies ++= Seq( "org.typelevel" %%% "otel4s-core-trace" % otel4sVersion, - "org.typelevel" %%% "otel4s-semconv" % otel4sVersion, - "org.typelevel" %%% "otel4s-semconv-experimental" % otel4sVersion, "org.scalameta" %%% "munit-scalacheck" % munitVersion % Test, "org.typelevel" %%% "munit-cats-effect-3" % munitCEVersion % Test ) diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala index 53e6495f..12b67f74 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala @@ -18,8 +18,9 @@ package feral.lambda.otel4s import feral.lambda.events.SqsRecord import org.typelevel.otel4s.Attribute -import org.typelevel.otel4s.semconv.experimental.attributes.FaasExperimentalAttributes._ -import org.typelevel.otel4s.semconv.experimental.attributes.MessagingExperimentalAttributes._ + +import LambdaMessageAttributes._ +import LambdaContextAttributes._ object SqsEventTraceAttributes { def apply(): List[Attribute[_]] = diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/FaasAttributes.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/FaasAttributes.scala index eed50f36..408fb608 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/FaasAttributes.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/FaasAttributes.scala @@ -18,24 +18,55 @@ package feral.lambda.otel4s import feral.lambda.Context import org.typelevel.otel4s.Attribute -import org.typelevel.otel4s.semconv.experimental.attributes.CloudExperimentalAttributes -import org.typelevel.otel4s.semconv.experimental.attributes.FaasExperimentalAttributes +import org.typelevel.otel4s.AttributeKey -object LambdaContextTraceAttributes { - val InvocationId = FaasExperimentalAttributes.FaasInvocationId - val FaasTrigger = FaasExperimentalAttributes.FaasTrigger +/** + * Temporary aliases for Lambda message-specific attributes in otel4s-semconv-experimental + */ +object LambdaMessageAttributes { + val MessagingSystem = AttributeKey.string("messaging.system") + object MessagingSystemValue { + object Sqs { + val value = "aws_sqs" + } + } + val MessagingOperation = AttributeKey.string("messaging.operation") + object MessagingOperationValue { + object Receive { + val value = "receive" + } + } + val MessagingMessageId = AttributeKey.string("messaging.message.id") + val MessagingDestinationName = AttributeKey.string("messaging.destination.name") +} + +/** + * Temporary aliases for Lambda platform attributes in otel4s-semconv-experimental + */ +object LambdaContextAttributes { + val FaasInvocationId = AttributeKey.string("faas.invocation_id") + val FaasTrigger = AttributeKey.string("faas.trigger") + object FaasTriggerValue { + object Pubsub { + val value = "pubsub" + } + } // ARN - val CloudResourceId = CloudExperimentalAttributes.CloudResourceId - // log stream name - val FaasInstance = FaasExperimentalAttributes.FaasInstance - val FaasMaxMemory = FaasExperimentalAttributes.FaasMaxMemory - val FaasName = FaasExperimentalAttributes.FaasName - val FaasVersion = FaasExperimentalAttributes.FaasVersion - val CloudProvider = CloudExperimentalAttributes.CloudProvider + val CloudResourceId = AttributeKey.string("cloud.resource_id") + val FaasInstance = AttributeKey.string("faas.instance") + val FaasMaxMemory = AttributeKey.long("faas.max_memory") + val FaasName = AttributeKey.string("faas.name") + val FaasVersion = AttributeKey.string("faas.version") + val CloudProvider = AttributeKey.string("cloud.provider") + object CloudProviderValue { + object Aws { + val value = "aws" + } + } def apply[F[_]](context: Context[F]): List[Attribute[_]] = { List( - CloudProvider(CloudExperimentalAttributes.CloudProviderValue.Aws.value), + CloudProvider(CloudProviderValue.Aws.value), CloudResourceId(context.invokedFunctionArn), FaasInstance(context.logStreamName), FaasMaxMemory(context.memoryLimitInMB.toLong), diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala index 72095d26..47fd6830 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala @@ -46,7 +46,7 @@ object TracedHandler { ): SpanOps[F] = Tracer[F] .spanBuilder(context.functionName) - .addAttributes(LambdaContextTraceAttributes(context)) + .addAttributes(LambdaContextAttributes(context)) .withSpanKind(attr.spanKind) .addAttributes(attr.attributes(event)) .build From a9613201066baabb5c153b433ce96d2e7a86370e Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Fri, 19 Apr 2024 18:35:36 +0100 Subject: [PATCH 20/64] Add attributes for other events --- .../feral/examples/SqsOtelJavaExample.scala | 4 +- ...{FaasAttributes.scala => Attributes.scala} | 6 +++ .../lambda/otel4s/EventAttributeSources.scala | 47 +++++++++++++++++-- .../feral/lambda/otel4s/EventAttributes.scala | 45 ++++++++++++++++-- 4 files changed, 91 insertions(+), 11 deletions(-) rename lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/{FaasAttributes.scala => Attributes.scala} (95%) diff --git a/examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala b/examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala index 300bbb69..3c50b71e 100644 --- a/examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala +++ b/examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala @@ -21,7 +21,7 @@ import _root_.feral.lambda.IOLambda import _root_.feral.lambda.Invocation import _root_.feral.lambda.events.SqsEvent import _root_.feral.lambda.events.SqsRecord -import _root_.feral.lambda.otel4s.SqsRecordTraceAttributes +import _root_.feral.lambda.otel4s.SqsRecordAttributes import _root_.feral.lambda.otel4s.TracedHandler import _root_.feral.lambda.otel4s._ import cats.Monad @@ -54,7 +54,7 @@ object SqsOtelExample extends IOLambda[SqsEvent, INothing] { event .records .traverse(record => - Tracer[F].span("handle-record", SqsRecordTraceAttributes(record)).surround { + Tracer[F].span("handle-record", SqsRecordAttributes(record)).surround { handleRecord[F](record) }) .as(None) diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/FaasAttributes.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/Attributes.scala similarity index 95% rename from lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/FaasAttributes.scala rename to lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/Attributes.scala index 408fb608..af369437 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/FaasAttributes.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/Attributes.scala @@ -50,6 +50,12 @@ object LambdaContextAttributes { object Pubsub { val value = "pubsub" } + object Datasource { + val value = "datasource" + } + object Http { + val value = "http" + } } // ARN val CloudResourceId = AttributeKey.string("cloud.resource_id") diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributeSources.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributeSources.scala index 0bdb9c1a..95c41fb9 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributeSources.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributeSources.scala @@ -33,14 +33,51 @@ trait EventAttributeSources { def spanKind: SpanKind = SpanKind.Consumer def attributes(e: SqsEvent): List[Attribute[_]] = - SqsEventTraceAttributes() + SqsEventAttributes() } - implicit def dynamoDbStreamEvent: EventSpanAttributes[DynamoDbStreamEvent] = ??? + implicit def dynamoDbStreamEvent: EventSpanAttributes[DynamoDbStreamEvent] = + new EventSpanAttributes[DynamoDbStreamEvent] { + def contextCarrier(e: DynamoDbStreamEvent): Map[String, String] = + Map.empty + + def spanKind: SpanKind = SpanKind.Consumer + + def attributes(e: DynamoDbStreamEvent): List[Attribute[_]] = + DynamoDbStreamEventAttributes() + } + + implicit def apiGatewayProxyEvent: EventSpanAttributes[ApiGatewayProxyEvent] = + new EventSpanAttributes[ApiGatewayProxyEvent] { + def contextCarrier(e: ApiGatewayProxyEvent): Map[String, String] = + e.headers.getOrElse(Map.empty).map { case (k, v) => (k.toString, v) } + + def spanKind: SpanKind = SpanKind.Server + + def attributes(e: ApiGatewayProxyEvent): List[Attribute[_]] = + ApiGatewayProxyEventAttributes() + } + + implicit def apiGatewayProxyEventV2: EventSpanAttributes[ApiGatewayProxyEventV2] = + new EventSpanAttributes[ApiGatewayProxyEventV2] { + def contextCarrier(e: ApiGatewayProxyEventV2): Map[String, String] = + e.headers.map { case (k, v) => (k.toString, v) } + + def spanKind: SpanKind = SpanKind.Server + + def attributes(e: ApiGatewayProxyEventV2): List[Attribute[_]] = + ApiGatewayProxyEventAttributes() + } + + implicit def s3BatchEvent: EventSpanAttributes[S3BatchEvent] = + new EventSpanAttributes[S3BatchEvent] { + def contextCarrier(e: S3BatchEvent): Map[String, String] = + Map.empty - implicit def apiGatewayProxyEvent: EventSpanAttributes[ApiGatewayProxyEvent] = ??? + def spanKind: SpanKind = SpanKind.Server - implicit def apiGatewayProxyEventV2: EventSpanAttributes[ApiGatewayProxyEventV2] = ??? + def attributes(e: S3BatchEvent): List[Attribute[_]] = + S3BatchEventAttributes(e) + } - implicit def s3BatchEvent: EventSpanAttributes[S3BatchEvent] = ??? } diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala index 12b67f74..a7a40e9c 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala @@ -16,13 +16,17 @@ package feral.lambda.otel4s +import feral.lambda.events.ApiGatewayProxyEvent +import feral.lambda.events.DynamoDbRecord +import feral.lambda.events.DynamoDbStreamEvent +import feral.lambda.events.S3BatchEvent import feral.lambda.events.SqsRecord import org.typelevel.otel4s.Attribute import LambdaMessageAttributes._ import LambdaContextAttributes._ -object SqsEventTraceAttributes { +object SqsEventAttributes { def apply(): List[Attribute[_]] = List( FaasTrigger(FaasTriggerValue.Pubsub.value), @@ -30,12 +34,45 @@ object SqsEventTraceAttributes { ) } -object SqsRecordTraceAttributes { +object SqsRecordAttributes { def apply(e: SqsRecord): List[Attribute[_]] = List( FaasTrigger(FaasTriggerValue.Pubsub.value), MessagingOperation(MessagingOperationValue.Receive.value), - MessagingMessageId(e.messageId), - MessagingDestinationName(e.eventSource) + MessagingMessageId(e.messageId) + ) +} + +object DynamoDbStreamEventAttributes { + def apply(): List[Attribute[_]] = + List( + FaasTrigger(FaasTriggerValue.Datasource.value), + MessagingSystem("aws_sqs") + ) +} + +object DynamoDbRecordAttributes { + def apply(e: DynamoDbRecord): List[Attribute[_]] = + List( + e.eventId.map(MessagingMessageId(_)), + Some(FaasTrigger(FaasTriggerValue.Datasource.value)), + Some(MessagingOperation(MessagingOperationValue.Receive.value)) + ).flatten +} + +object ApiGatewayProxyEventAttributes { + def apply(): List[Attribute[_]] = + List( + FaasTrigger(FaasTriggerValue.Http.value), + MessagingOperation(MessagingOperationValue.Receive.value) + ) +} + +object S3BatchEventAttributes { + def apply(e: S3BatchEvent): List[Attribute[_]] = + List( + MessagingMessageId(e.invocationId), + FaasTrigger(FaasTriggerValue.Datasource.value), + MessagingOperation(MessagingOperationValue.Receive.value) ) } From 96ce0a968d79ee5d218754274c0859b52bc6cb5d Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Fri, 19 Apr 2024 18:36:33 +0100 Subject: [PATCH 21/64] Remove unused imports --- .../src/main/scala/feral/lambda/otel4s/EventAttributes.scala | 2 -- 1 file changed, 2 deletions(-) diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala index a7a40e9c..3d0564eb 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala @@ -16,9 +16,7 @@ package feral.lambda.otel4s -import feral.lambda.events.ApiGatewayProxyEvent import feral.lambda.events.DynamoDbRecord -import feral.lambda.events.DynamoDbStreamEvent import feral.lambda.events.S3BatchEvent import feral.lambda.events.SqsRecord import org.typelevel.otel4s.Attribute From 68ea9236fc49df496d5bf3db677e63ff6bbdbc23 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Wed, 8 May 2024 21:11:29 +0100 Subject: [PATCH 22/64] Version bumps (to appease sbt 1.10) --- build.sbt | 6 +++--- .../feral/lambda/http4s/ApiGatewayProxyHandlerSuite.scala | 2 +- .../shared/src/main/scala-3/feral/lambda/invocations.scala | 2 +- project/plugins.sbt | 2 +- .../src/sbt-test/lambda-js-plugin/iolambda-simple/build.sbt | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/build.sbt b/build.sbt index 4b9f7bc6..1d325b34 100644 --- a/build.sbt +++ b/build.sbt @@ -46,8 +46,8 @@ ThisBuild / githubWorkflowBuildPreamble += cond = Some("matrix.project == 'rootJS'") ) -val Scala212 = "2.12.18" -val Scala213 = "2.13.12" +val Scala212 = "2.12.19" +val Scala213 = "2.13.14" val Scala3 = "3.3.3" ThisBuild / crossScalaVersions := Seq(Scala212, Scala3, Scala213) @@ -59,7 +59,7 @@ val natchezVersion = "0.3.5" val munitVersion = "0.7.29" val munitCEVersion = "1.0.7" val scalacheckEffectVersion = "1.0.4" -val otel4sVersion = "0.6.0" +val otel4sVersion = "0.7.0" lazy val commonSettings = Seq( crossScalaVersions := Seq(Scala3, Scala213) diff --git a/lambda-http4s/src/test/scala/feral/lambda/http4s/ApiGatewayProxyHandlerSuite.scala b/lambda-http4s/src/test/scala/feral/lambda/http4s/ApiGatewayProxyHandlerSuite.scala index 72602062..fbb700db 100644 --- a/lambda-http4s/src/test/scala/feral/lambda/http4s/ApiGatewayProxyHandlerSuite.scala +++ b/lambda-http4s/src/test/scala/feral/lambda/http4s/ApiGatewayProxyHandlerSuite.scala @@ -20,7 +20,7 @@ package http4s import cats.effect.IO import cats.syntax.all._ import feral.lambda.events.ApiGatewayProxyEvent -import feral.lambda.events.ApiGatewayProxyEventSuite.* +import feral.lambda.events.ApiGatewayProxyEventSuite._ import munit.CatsEffectSuite import org.http4s.Headers import org.http4s.HttpApp diff --git a/lambda/shared/src/main/scala-3/feral/lambda/invocations.scala b/lambda/shared/src/main/scala-3/feral/lambda/invocations.scala index f432eea5..2f388a1a 100644 --- a/lambda/shared/src/main/scala-3/feral/lambda/invocations.scala +++ b/lambda/shared/src/main/scala-3/feral/lambda/invocations.scala @@ -16,7 +16,7 @@ package feral.lambda -import events._ +import events.* type ApiGatewayProxyInvocation[F[_]] = Invocation[F, ApiGatewayProxyEvent] type ApiGatewayProxyInvocationV2[F[_]] = Invocation[F, ApiGatewayProxyEventV2] diff --git a/project/plugins.sbt b/project/plugins.sbt index abc884f6..4b385b9f 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -1,4 +1,4 @@ -val sbtlTlV = "0.6.7" +val sbtlTlV = "0.7.1" addSbtPlugin("org.typelevel" % "sbt-typelevel" % sbtlTlV) addSbtPlugin("org.typelevel" % "sbt-typelevel-scalafix" % sbtlTlV) addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.16.0") diff --git a/sbt-lambda/src/sbt-test/lambda-js-plugin/iolambda-simple/build.sbt b/sbt-lambda/src/sbt-test/lambda-js-plugin/iolambda-simple/build.sbt index c72b2d61..ddf1e789 100644 --- a/sbt-lambda/src/sbt-test/lambda-js-plugin/iolambda-simple/build.sbt +++ b/sbt-lambda/src/sbt-test/lambda-js-plugin/iolambda-simple/build.sbt @@ -1,2 +1,2 @@ -scalaVersion := "2.13.12" +scalaVersion := "2.13.14" enablePlugins(LambdaJSPlugin) From b2fcbf2d51d3635d6bbd0e597b9a2258a91c1303 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Wed, 8 May 2024 21:16:12 +0100 Subject: [PATCH 23/64] Add http4s-otel4s-middleware to example --- build.sbt | 3 ++- .../main/scala/feral/examples/SqsOtelJavaExample.scala | 9 ++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/build.sbt b/build.sbt index 1d325b34..9b91504c 100644 --- a/build.sbt +++ b/build.sbt @@ -211,7 +211,8 @@ lazy val examples = crossProject(JSPlatform, JVMPlatform) "org.http4s" %%% "http4s-ember-client" % http4sVersion, "org.tpolecat" %%% "natchez-xray" % natchezVersion, "org.tpolecat" %%% "natchez-http4s" % "0.5.0", - "org.tpolecat" %%% "skunk-core" % "0.6.3" + "org.tpolecat" %%% "skunk-core" % "0.6.3", + "org.http4s" %%% "http4s-otel4s-middleware" % "0.6.0" ) ) .settings(commonSettings) diff --git a/examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala b/examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala index 3c50b71e..02066db9 100644 --- a/examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala +++ b/examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala @@ -16,6 +16,7 @@ package feral.examples +import org.http4s.otel4s.middleware.ClientMiddleware import _root_.feral.lambda.INothing import _root_.feral.lambda.IOLambda import _root_.feral.lambda.Invocation @@ -40,7 +41,7 @@ object SqsOtelExample extends IOLambda[SqsEvent, INothing] { implicit tracer: Tracer[IO] => for { client <- EmberClientBuilder.default[IO].build - tracedClient = clientTraceMiddleware(client) + tracedClient = ClientMiddleware.default[IO].build(client) } yield { implicit inv: Invocation[IO, SqsEvent] => TracedHandler[IO, SqsEvent, INothing]( handleEvent[IO](tracedClient) @@ -66,10 +67,4 @@ object SqsOtelExample extends IOLambda[SqsEvent, INothing] { } } - // stub client middleware while http4s-otel-middleware catches up to otel4s - // 0.5 - private def clientTraceMiddleware[F[_]](client: Client[F])(implicit @unused T: Tracer[IO]) = { - client - } - } From 8f06d7495ce7f7a724a4920c30e8ff46b3471419 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Wed, 8 May 2024 21:20:03 +0100 Subject: [PATCH 24/64] Fix import order --- .../jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala b/examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala index 02066db9..2f79ac62 100644 --- a/examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala +++ b/examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala @@ -16,7 +16,6 @@ package feral.examples -import org.http4s.otel4s.middleware.ClientMiddleware import _root_.feral.lambda.INothing import _root_.feral.lambda.IOLambda import _root_.feral.lambda.Invocation @@ -30,6 +29,7 @@ import cats.effect.IO import cats.syntax.all._ import org.http4s.client.Client import org.http4s.ember.client.EmberClientBuilder +import org.http4s.otel4s.middleware.ClientMiddleware import org.typelevel.otel4s.oteljava.OtelJava import org.typelevel.otel4s.trace.Tracer import org.typelevel.scalaccompat.annotation.unused From 4c1cbb3069c58401cce8dca0b1a596004e554751 Mon Sep 17 00:00:00 2001 From: Maksym Ochenashko Date: Fri, 10 May 2024 11:30:39 +0300 Subject: [PATCH 25/64] use otel4s-sdk-trace-testkit to run tests across all platforms --- build.sbt | 4 +--- .../test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala | 6 +++--- 2 files changed, 4 insertions(+), 6 deletions(-) rename lambda-otel4s/{jvm => shared}/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala (96%) diff --git a/build.sbt b/build.sbt index 9b91504c..ae533fed 100644 --- a/build.sbt +++ b/build.sbt @@ -193,14 +193,12 @@ lazy val lambdaOtel4s = crossProject(JSPlatform, JVMPlatform) name := "feral-lambda-otel4s", libraryDependencies ++= Seq( "org.typelevel" %%% "otel4s-core-trace" % otel4sVersion, + "org.typelevel" %%% "otel4s-sdk-trace-testkit" % otel4sVersion % Test, "org.scalameta" %%% "munit-scalacheck" % munitVersion % Test, "org.typelevel" %%% "munit-cats-effect-3" % munitCEVersion % Test ) ) .settings(commonSettings) - .jvmSettings(libraryDependencies ++= Seq( - "org.typelevel" %%% "otel4s-oteljava-trace-testkit" % otel4sVersion % Test - )) .dependsOn(lambda) lazy val examples = crossProject(JSPlatform, JVMPlatform) diff --git a/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala b/lambda-otel4s/shared/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala similarity index 96% rename from lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala rename to lambda-otel4s/shared/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala index 34921fc4..b3815ae4 100644 --- a/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala +++ b/lambda-otel4s/shared/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala @@ -26,7 +26,7 @@ import io.circe.Encoder import io.circe.syntax._ import munit.CatsEffectSuite import org.typelevel.otel4s.Attribute -import org.typelevel.otel4s.oteljava.testkit.trace.TracesTestkit +import org.typelevel.otel4s.sdk.testkit.trace.TracesTestkit import org.typelevel.otel4s.trace.SpanKind import java.io.ByteArrayInputStream @@ -80,7 +80,7 @@ class TracedHandlerSuite extends CatsEffectSuite { _ <- IO { assertEquals(res, "\"body\"".toString) assertEquals(spans.length, 1) - assertEquals(spans.headOption.map(_.getName()), Some(functionName)) + assertEquals(spans.headOption.map(_.name), Some(functionName)) } } yield () } @@ -122,7 +122,7 @@ class TracedHandlerSuite extends CatsEffectSuite { _ <- IO { assertEquals(res.length, chars.length) assertEquals(spans.length, chars.length) - assertEquals(spans.map(_.getName()), expectedSpanNames) + assertEquals(spans.map(_.name), expectedSpanNames) } } yield () } From 51aad4a13ce8b0345cd9f4bc4e527833f3729026 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Sun, 15 Sep 2024 22:55:34 +0100 Subject: [PATCH 26/64] Move TracedHandlerSuite to JVM --- .../src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename lambda-otel4s/{shared => jvm}/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala (100%) diff --git a/lambda-otel4s/shared/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala b/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala similarity index 100% rename from lambda-otel4s/shared/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala rename to lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala From a208070af97272343b20b250ac52eab9006f0cf9 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Sun, 15 Sep 2024 23:03:06 +0100 Subject: [PATCH 27/64] Update workflows --- .github/workflows/ci.yml | 4 ++-- build.sbt | 1 - .../test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala | 2 -- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5c1b4daf..75d37211 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -118,11 +118,11 @@ jobs: - name: Make target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') - run: mkdir -p lambda-cloudformation-custom-resource/.js/target lambda-http4s/.jvm/target unidocs/target lambda-http4s/.js/target lambda/js/target scalafix/rules/target lambda/jvm/target sbt-lambda/target google-cloud-http4s/jvm/target lambda-cloudformation-custom-resource/.jvm/target google-cloud-http4s/js/target project/target + run: mkdir -p lambda-natchez/jvm/target lambda-natchez/js/target lambda-cloudformation-custom-resource/.js/target lambda-http4s/.jvm/target unidocs/target lambda-http4s/.js/target lambda/js/target scalafix/rules/target lambda/jvm/target lambda-otel4s/jvm/target sbt-lambda/target lambda-otel4s/js/target google-cloud-http4s/jvm/target lambda-cloudformation-custom-resource/.jvm/target google-cloud-http4s/js/target project/target - name: Compress target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') - run: tar cf targets.tar lambda-cloudformation-custom-resource/.js/target lambda-http4s/.jvm/target unidocs/target lambda-http4s/.js/target lambda/js/target scalafix/rules/target lambda/jvm/target sbt-lambda/target google-cloud-http4s/jvm/target lambda-cloudformation-custom-resource/.jvm/target google-cloud-http4s/js/target project/target + run: tar cf targets.tar lambda-natchez/jvm/target lambda-natchez/js/target lambda-cloudformation-custom-resource/.js/target lambda-http4s/.jvm/target unidocs/target lambda-http4s/.js/target lambda/js/target scalafix/rules/target lambda/jvm/target lambda-otel4s/jvm/target sbt-lambda/target lambda-otel4s/js/target google-cloud-http4s/jvm/target lambda-cloudformation-custom-resource/.jvm/target google-cloud-http4s/js/target project/target - name: Upload target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') diff --git a/build.sbt b/build.sbt index 69130ece..bc2a1984 100644 --- a/build.sbt +++ b/build.sbt @@ -15,7 +15,6 @@ */ import com.typesafe.tools.mima.core._ -import xerial.sbt.Sonatype.sonatype01 name := "feral" diff --git a/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala b/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala index b3815ae4..85f47db5 100644 --- a/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala +++ b/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala @@ -168,6 +168,4 @@ object TracedHandlerSuite { } } - def tracedLambda(allocationCounter: AtomicInteger, invokeCounter: AtomicInteger) = {} - } From 2d435fdecc1db9c9d9a271dd99d4110155399e8e Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Sun, 15 Sep 2024 23:04:08 +0100 Subject: [PATCH 28/64] Swap module order --- build.sbt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index bc2a1984..405fa4ed 100644 --- a/build.sbt +++ b/build.sbt @@ -68,10 +68,10 @@ lazy val root = tlCrossRootProject .aggregate( lambda, - lambdaNatchez, - lambdaOtel4s, lambdaHttp4s, lambdaCloudFormationCustomResource, + lambdaNatchez, + lambdaOtel4s, googleCloudHttp4s, examples, unidocs From ad3dbaecd6f1b64097ded89f02af3ee6cbc3a72e Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Sun, 15 Sep 2024 23:05:09 +0100 Subject: [PATCH 29/64] Fix TracedHandlerSuite class name --- .../test/scala/feral/lambda/natchez/TracedHandlerSuite.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lambda-natchez/shared/src/test/scala/feral/lambda/natchez/TracedHandlerSuite.scala b/lambda-natchez/shared/src/test/scala/feral/lambda/natchez/TracedHandlerSuite.scala index 2939fa22..bd74ff3b 100644 --- a/lambda-natchez/shared/src/test/scala/feral/lambda/natchez/TracedHandlerSuite.scala +++ b/lambda-natchez/shared/src/test/scala/feral/lambda/natchez/TracedHandlerSuite.scala @@ -27,7 +27,7 @@ import natchez.Trace import scala.annotation.nowarn -class TracedLambdaSuite { +class TracedHandlerSuite { @nowarn def syntaxTest = { // Checking for compilation, nothing more From fe22625eabd906151ef1d7e83e029d15c720d1e2 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Sun, 15 Sep 2024 23:36:59 +0100 Subject: [PATCH 30/64] Add JS TracedHandler suite --- .../lambda/otel4s/TracedHandlerSuite.scala | 94 +++++++++++++++++++ .../lambda/otel4s/TracedHandlerSuite.scala | 2 + .../lambda/otel4s/EventSpanAttributes.scala | 2 +- 3 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala diff --git a/lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala b/lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala new file mode 100644 index 00000000..f870ce7f --- /dev/null +++ b/lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala @@ -0,0 +1,94 @@ +/* + * Copyright 2021 Typelevel + * + * Licensed 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 feral.lambda +package otel4s + +import cats.effect.IO +import cats.effect.kernel.Resource +import cats.syntax.all._ +import munit.CatsEffectSuite +import org.typelevel.otel4s.trace.SpanKind + +import java.util.concurrent.atomic.AtomicInteger +import scala.scalajs.js +import org.typelevel.otel4s.sdk.testkit.trace.TracesTestkit +import feral.lambda.otel4s.TracedHandler +import feral.lambda.otel4s.EventSpanAttributes + +class TracedHandlerSuite extends CatsEffectSuite { + import TracedHandlerSuite._ + + val fixture = ResourceFixture(TracesTestkit.inMemory[IO]()) + + fixture.test("single root span is created for single invocation") { traces => + traces.tracerProvider.tracer("test-tracer").get.flatMap { implicit tracer => + val allocationCounter = new AtomicInteger + val invokeCounter = new AtomicInteger + + val lambda = new IOLambda[String, String] { + def handler = + Resource.eval(IO(allocationCounter.getAndIncrement())).as { implicit inv => + def fn(ev: String): IO[Option[String]] = + for { + _ <- IO(invokeCounter.getAndIncrement()) + res = Some(ev) + } yield res + + TracedHandler(fn) + } + } + + val event = "event" + + val functionName = "test-function-name" + val run = IO.fromPromise(IO(lambda.handlerFn(event, new DummyContext(functionName)))) + + for { + res <- run + spans <- traces.finishedSpans + _ <- IO { + assertEquals(spans.length, 1) + assertEquals(spans.headOption.map(_.name), Some(functionName)) + assertEquals(allocationCounter.get(), 1) + assertEquals(invokeCounter.get(), 1) + assertEquals(res, event.asInstanceOf[js.UndefOr[js.Any]]) + } + } yield () + } + } + + class DummyContext(fnName: String) extends facade.Context { + def functionName = fnName + def functionVersion = "" + def invokedFunctionArn = "" + def memoryLimitInMB = "0" + def awsRequestId = "" + def logGroupName = "" + def logStreamName = "" + def identity = js.undefined + def clientContext = js.undefined + def getRemainingTimeInMillis(): Double = 0 + } + +} + +object TracedHandlerSuite { + + implicit val attr: EventSpanAttributes[String] = + EventSpanAttributes.empty[String](SpanKind.Internal) + +} diff --git a/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala b/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala index 85f47db5..07c54b13 100644 --- a/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala +++ b/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala @@ -81,6 +81,8 @@ class TracedHandlerSuite extends CatsEffectSuite { assertEquals(res, "\"body\"".toString) assertEquals(spans.length, 1) assertEquals(spans.headOption.map(_.name), Some(functionName)) + assertEquals(allocationCounter.get(), 1) + assertEquals(invokeCounter.get(), 1) } } yield () } diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventSpanAttributes.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventSpanAttributes.scala index 73c5e261..22ad5f6e 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventSpanAttributes.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventSpanAttributes.scala @@ -26,7 +26,7 @@ protected trait EventSpanAttributes[E] { def attributes(e: E): List[Attribute[_]] } -object EventSpanAttributes { +protected object EventSpanAttributes { def empty[E](sk: SpanKind): EventSpanAttributes[E] = new EventSpanAttributes[E] { def contextCarrier(e: E): Map[String, String] = From c8f2913b045360e4e3067062dc7f1810e6ae1c3c Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Sun, 15 Sep 2024 23:38:46 +0100 Subject: [PATCH 31/64] Remove unused imports --- .../test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala b/lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala index f870ce7f..90c8a0ac 100644 --- a/lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala +++ b/lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala @@ -21,13 +21,11 @@ import cats.effect.IO import cats.effect.kernel.Resource import cats.syntax.all._ import munit.CatsEffectSuite +import org.typelevel.otel4s.sdk.testkit.trace.TracesTestkit import org.typelevel.otel4s.trace.SpanKind import java.util.concurrent.atomic.AtomicInteger import scala.scalajs.js -import org.typelevel.otel4s.sdk.testkit.trace.TracesTestkit -import feral.lambda.otel4s.TracedHandler -import feral.lambda.otel4s.EventSpanAttributes class TracedHandlerSuite extends CatsEffectSuite { import TracedHandlerSuite._ From e8fbf9dc361f08094b565634bb1ce1fe042a39eb Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Sun, 15 Sep 2024 23:42:32 +0100 Subject: [PATCH 32/64] Update base version --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 405fa4ed..cb96ae86 100644 --- a/build.sbt +++ b/build.sbt @@ -18,7 +18,7 @@ import com.typesafe.tools.mima.core._ name := "feral" -ThisBuild / tlBaseVersion := "0.3" +ThisBuild / tlBaseVersion := "0.4" ThisBuild / startYear := Some(2021) ThisBuild / developers := List( From 501e76a6356c88d8ba86d6577ef5c67c7cd89893 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Sun, 15 Sep 2024 23:52:57 +0100 Subject: [PATCH 33/64] Update otel4s to 0.8 --- build.sbt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index cb96ae86..f564d0ba 100644 --- a/build.sbt +++ b/build.sbt @@ -58,7 +58,7 @@ val natchezVersion = "0.3.6" val munitVersion = "0.7.29" val munitCEVersion = "1.0.7" val scalacheckEffectVersion = "1.0.4" -val otel4sVersion = "0.7.0" +val otel4sVersion = "0.8.0" lazy val commonSettings = Seq( crossScalaVersions := Seq(Scala3, Scala213) @@ -210,7 +210,7 @@ lazy val examples = crossProject(JSPlatform, JVMPlatform) "org.tpolecat" %%% "natchez-xray" % natchezVersion, "org.tpolecat" %%% "natchez-http4s" % "0.6.0", "org.tpolecat" %%% "skunk-core" % "0.6.3", - "org.http4s" %%% "http4s-otel4s-middleware" % "0.6.0" + "org.http4s" %%% "http4s-otel4s-middleware" % "0.8.0" ) ) .settings(commonSettings) From 5e0597283888df1f2b7e83438f64439a0a63000d Mon Sep 17 00:00:00 2001 From: Maksym Ochenashko Date: Mon, 16 Sep 2024 09:58:37 +0300 Subject: [PATCH 34/64] Lambda otel4s: test span attributes --- build.sbt | 1 + .../lambda/otel4s/TracedHandlerSuite.scala | 13 ++++++ .../lambda/otel4s/TracedHandlerSuite.scala | 41 ++++++++++++++++--- .../feral/lambda/otel4s/Attributes.scala | 2 +- 4 files changed, 51 insertions(+), 6 deletions(-) diff --git a/build.sbt b/build.sbt index f564d0ba..0efc505d 100644 --- a/build.sbt +++ b/build.sbt @@ -194,6 +194,7 @@ lazy val lambdaOtel4s = crossProject(JSPlatform, JVMPlatform) libraryDependencies ++= Seq( "org.typelevel" %%% "otel4s-core-trace" % otel4sVersion, "org.typelevel" %%% "otel4s-sdk-trace-testkit" % otel4sVersion % Test, + "org.typelevel" %%% "otel4s-semconv-experimental" % otel4sVersion % Test, "org.scalameta" %%% "munit-scalacheck" % munitVersion % Test, "org.typelevel" %%% "munit-cats-effect-3" % munitCEVersion % Test ) diff --git a/lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala b/lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala index 90c8a0ac..f98d7009 100644 --- a/lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala +++ b/lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala @@ -21,7 +21,10 @@ import cats.effect.IO import cats.effect.kernel.Resource import cats.syntax.all._ import munit.CatsEffectSuite +import org.typelevel.otel4s.AttributeKey import org.typelevel.otel4s.sdk.testkit.trace.TracesTestkit +import org.typelevel.otel4s.semconv.experimental.attributes.CloudExperimentalAttributes +import org.typelevel.otel4s.semconv.experimental.attributes.FaasExperimentalAttributes import org.typelevel.otel4s.trace.SpanKind import java.util.concurrent.atomic.AtomicInteger @@ -55,12 +58,22 @@ class TracedHandlerSuite extends CatsEffectSuite { val functionName = "test-function-name" val run = IO.fromPromise(IO(lambda.handlerFn(event, new DummyContext(functionName)))) + val attributeKeys = Set[AttributeKey[_]]( + FaasExperimentalAttributes.FaasName, + FaasExperimentalAttributes.FaasVersion, + FaasExperimentalAttributes.FaasInstance, + FaasExperimentalAttributes.FaasMaxMemory, + CloudExperimentalAttributes.CloudProvider, + CloudExperimentalAttributes.CloudResourceId + ) + for { res <- run spans <- traces.finishedSpans _ <- IO { assertEquals(spans.length, 1) assertEquals(spans.headOption.map(_.name), Some(functionName)) + assertEquals(spans.headOption.map(_.attributes.map(_.key).toSet), Some(attributeKeys)) assertEquals(allocationCounter.get(), 1) assertEquals(invokeCounter.get(), 1) assertEquals(res, event.asInstanceOf[js.UndefOr[js.Any]]) diff --git a/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala b/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala index 07c54b13..44e04f29 100644 --- a/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala +++ b/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala @@ -26,7 +26,11 @@ import io.circe.Encoder import io.circe.syntax._ import munit.CatsEffectSuite import org.typelevel.otel4s.Attribute +import org.typelevel.otel4s.Attributes import org.typelevel.otel4s.sdk.testkit.trace.TracesTestkit +import org.typelevel.otel4s.semconv.experimental.attributes.CloudExperimentalAttributes +import org.typelevel.otel4s.semconv.experimental.attributes.CloudExperimentalAttributes.CloudProviderValue +import org.typelevel.otel4s.semconv.experimental.attributes.FaasExperimentalAttributes import org.typelevel.otel4s.trace.SpanKind import java.io.ByteArrayInputStream @@ -50,6 +54,11 @@ class TracedHandlerSuite extends CatsEffectSuite { val fixture = ResourceFixture(TracesTestkit.inMemory[IO]()) + private val memoryLimitInMB = 1024 + private val functionVersion = "1.0.1" + private val logStreamNameArn = "arn:aws:logs:us-east-1:123456789012:log-group:/aws/lambda/my-function:*" + private val functionArn = "arn:aws:lambda:us-west-2:123456789012:function:test-function-name:PROD" + fixture.test("single root span is created for single invocation") { traces => traces.tracerProvider.tracer("test-tracer").get.flatMap { implicit tracer => val allocationCounter = new AtomicInteger @@ -74,13 +83,23 @@ class TracedHandlerSuite extends CatsEffectSuite { val functionName = "test-function-name" val run = IO(lambda.handleRequestHelper(payload, DummyContext(functionName))) + val attributes = Attributes( + FaasExperimentalAttributes.FaasName(functionName), + FaasExperimentalAttributes.FaasVersion(functionVersion), + FaasExperimentalAttributes.FaasInstance(logStreamNameArn), + FaasExperimentalAttributes.FaasMaxMemory(memoryLimitInMB * 1024L * 1024L), + CloudExperimentalAttributes.CloudProvider(CloudProviderValue.Aws.value), + CloudExperimentalAttributes.CloudResourceId(functionArn) + ) + for { res <- run spans <- traces.finishedSpans _ <- IO { - assertEquals(res, "\"body\"".toString) + assertEquals(res, "\"body\"") assertEquals(spans.length, 1) assertEquals(spans.headOption.map(_.name), Some(functionName)) + assertEquals(spans.headOption.map(_.attributes), Some(attributes)) assertEquals(allocationCounter.get(), 1) assertEquals(invokeCounter.get(), 1) } @@ -118,6 +137,17 @@ class TracedHandlerSuite extends CatsEffectSuite { val expectedSpanNames = List.fill(3)(functionName) + val expectedAttributes = List.fill(3)( + Attributes( + FaasExperimentalAttributes.FaasName(functionName), + FaasExperimentalAttributes.FaasVersion(functionVersion), + FaasExperimentalAttributes.FaasInstance(logStreamNameArn), + FaasExperimentalAttributes.FaasMaxMemory(memoryLimitInMB * 1024L * 1024L), + CloudExperimentalAttributes.CloudProvider(CloudProviderValue.Aws.value), + CloudExperimentalAttributes.CloudResourceId(functionArn) + ) + ) + for { res <- run spans <- traces.finishedSpans @@ -125,6 +155,7 @@ class TracedHandlerSuite extends CatsEffectSuite { assertEquals(res.length, chars.length) assertEquals(spans.length, chars.length) assertEquals(spans.map(_.name), expectedSpanNames) + assertEquals(spans.map(_.attributes), expectedAttributes) } } yield () } @@ -133,14 +164,14 @@ class TracedHandlerSuite extends CatsEffectSuite { case class DummyContext(functionName: String) extends runtime.Context { override def getAwsRequestId(): String = "" override def getLogGroupName(): String = "" - override def getLogStreamName(): String = "" + override def getLogStreamName(): String = logStreamNameArn override def getFunctionName(): String = functionName - override def getFunctionVersion(): String = "" - override def getInvokedFunctionArn(): String = "" + override def getFunctionVersion(): String = functionVersion + override def getInvokedFunctionArn(): String = functionArn override def getIdentity(): runtime.CognitoIdentity = null override def getClientContext(): runtime.ClientContext = null override def getRemainingTimeInMillis(): Int = Int.MaxValue - override def getMemoryLimitInMB(): Int = 0 + override def getMemoryLimitInMB(): Int = memoryLimitInMB override def getLogger(): runtime.LambdaLogger = null } diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/Attributes.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/Attributes.scala index af369437..a5680fbc 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/Attributes.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/Attributes.scala @@ -75,7 +75,7 @@ object LambdaContextAttributes { CloudProvider(CloudProviderValue.Aws.value), CloudResourceId(context.invokedFunctionArn), FaasInstance(context.logStreamName), - FaasMaxMemory(context.memoryLimitInMB.toLong), + FaasMaxMemory(context.memoryLimitInMB.toLong * 1024 * 1024), FaasName(context.functionName), FaasVersion(context.functionVersion) ) From 81ce08d5b1025b1ae9f948da0e72f21c8704e72e Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Mon, 16 Sep 2024 10:23:12 +0100 Subject: [PATCH 35/64] Format --- .../test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala b/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala index 44e04f29..5fd8bad3 100644 --- a/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala +++ b/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala @@ -56,8 +56,10 @@ class TracedHandlerSuite extends CatsEffectSuite { private val memoryLimitInMB = 1024 private val functionVersion = "1.0.1" - private val logStreamNameArn = "arn:aws:logs:us-east-1:123456789012:log-group:/aws/lambda/my-function:*" - private val functionArn = "arn:aws:lambda:us-west-2:123456789012:function:test-function-name:PROD" + private val logStreamNameArn = + "arn:aws:logs:us-east-1:123456789012:log-group:/aws/lambda/my-function:*" + private val functionArn = + "arn:aws:lambda:us-west-2:123456789012:function:test-function-name:PROD" fixture.test("single root span is created for single invocation") { traces => traces.tracerProvider.tracer("test-tracer").get.flatMap { implicit tracer => From 84d303b03e47589337e586454be410b5bac2df75 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Mon, 16 Sep 2024 11:40:02 +0100 Subject: [PATCH 36/64] Swap List[Attribute[_]] to Attributes --- .../lambda/otel4s/TracedHandlerSuite.scala | 2 +- .../feral/lambda/otel4s/Attributes.scala | 4 +-- .../lambda/otel4s/EventAttributeSources.scala | 12 +++---- .../feral/lambda/otel4s/EventAttributes.scala | 33 ++++++++++--------- .../lambda/otel4s/EventSpanAttributes.scala | 8 ++--- 5 files changed, 30 insertions(+), 29 deletions(-) diff --git a/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala b/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala index 5fd8bad3..31e9c1e2 100644 --- a/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala +++ b/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala @@ -198,7 +198,7 @@ object TracedHandlerSuite { override def spanKind: SpanKind = SpanKind.Consumer - override def attributes(e: TestEvent): List[Attribute[_]] = List.empty + override def attributes(e: TestEvent): Attributes = Attributes.empty } } diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/Attributes.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/Attributes.scala index a5680fbc..f314fc76 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/Attributes.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/Attributes.scala @@ -23,7 +23,7 @@ import org.typelevel.otel4s.AttributeKey /** * Temporary aliases for Lambda message-specific attributes in otel4s-semconv-experimental */ -object LambdaMessageAttributes { +private[otel4s] object LambdaMessageAttributes { val MessagingSystem = AttributeKey.string("messaging.system") object MessagingSystemValue { object Sqs { @@ -43,7 +43,7 @@ object LambdaMessageAttributes { /** * Temporary aliases for Lambda platform attributes in otel4s-semconv-experimental */ -object LambdaContextAttributes { +private[otel4s] object LambdaContextAttributes { val FaasInvocationId = AttributeKey.string("faas.invocation_id") val FaasTrigger = AttributeKey.string("faas.trigger") object FaasTriggerValue { diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributeSources.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributeSources.scala index 95c41fb9..60732c36 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributeSources.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributeSources.scala @@ -21,7 +21,7 @@ import feral.lambda.events.ApiGatewayProxyEventV2 import feral.lambda.events.DynamoDbStreamEvent import feral.lambda.events.S3BatchEvent import feral.lambda.events.SqsEvent -import org.typelevel.otel4s.Attribute +import org.typelevel.otel4s.Attributes import org.typelevel.otel4s.trace.SpanKind trait EventAttributeSources { @@ -32,7 +32,7 @@ trait EventAttributeSources { def spanKind: SpanKind = SpanKind.Consumer - def attributes(e: SqsEvent): List[Attribute[_]] = + def attributes(e: SqsEvent): Attributes = SqsEventAttributes() } @@ -43,7 +43,7 @@ trait EventAttributeSources { def spanKind: SpanKind = SpanKind.Consumer - def attributes(e: DynamoDbStreamEvent): List[Attribute[_]] = + def attributes(e: DynamoDbStreamEvent): Attributes = DynamoDbStreamEventAttributes() } @@ -54,7 +54,7 @@ trait EventAttributeSources { def spanKind: SpanKind = SpanKind.Server - def attributes(e: ApiGatewayProxyEvent): List[Attribute[_]] = + def attributes(e: ApiGatewayProxyEvent): Attributes = ApiGatewayProxyEventAttributes() } @@ -65,7 +65,7 @@ trait EventAttributeSources { def spanKind: SpanKind = SpanKind.Server - def attributes(e: ApiGatewayProxyEventV2): List[Attribute[_]] = + def attributes(e: ApiGatewayProxyEventV2): Attributes = ApiGatewayProxyEventAttributes() } @@ -76,7 +76,7 @@ trait EventAttributeSources { def spanKind: SpanKind = SpanKind.Server - def attributes(e: S3BatchEvent): List[Attribute[_]] = + def attributes(e: S3BatchEvent): Attributes = S3BatchEventAttributes(e) } diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala index 3d0564eb..5f3eec48 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala @@ -20,21 +20,22 @@ import feral.lambda.events.DynamoDbRecord import feral.lambda.events.S3BatchEvent import feral.lambda.events.SqsRecord import org.typelevel.otel4s.Attribute +import org.typelevel.otel4s.Attributes import LambdaMessageAttributes._ import LambdaContextAttributes._ object SqsEventAttributes { - def apply(): List[Attribute[_]] = - List( + def apply(): Attributes = + Attributes( FaasTrigger(FaasTriggerValue.Pubsub.value), MessagingSystem("aws_sqs") ) } object SqsRecordAttributes { - def apply(e: SqsRecord): List[Attribute[_]] = - List( + def apply(e: SqsRecord): Attributes = + Attributes( FaasTrigger(FaasTriggerValue.Pubsub.value), MessagingOperation(MessagingOperationValue.Receive.value), MessagingMessageId(e.messageId) @@ -42,33 +43,33 @@ object SqsRecordAttributes { } object DynamoDbStreamEventAttributes { - def apply(): List[Attribute[_]] = - List( + def apply(): Attributes = + Attributes( FaasTrigger(FaasTriggerValue.Datasource.value), MessagingSystem("aws_sqs") ) } object DynamoDbRecordAttributes { - def apply(e: DynamoDbRecord): List[Attribute[_]] = - List( - e.eventId.map(MessagingMessageId(_)), - Some(FaasTrigger(FaasTriggerValue.Datasource.value)), - Some(MessagingOperation(MessagingOperationValue.Receive.value)) - ).flatten + def apply(e: DynamoDbRecord): Attributes = { + Attributes( + FaasTrigger(FaasTriggerValue.Datasource.value), + MessagingOperation(MessagingOperationValue.Receive.value) + ) ++ e.eventId.map(MessagingMessageId(_)) + } } object ApiGatewayProxyEventAttributes { - def apply(): List[Attribute[_]] = - List( + def apply(): Attributes = + Attributes( FaasTrigger(FaasTriggerValue.Http.value), MessagingOperation(MessagingOperationValue.Receive.value) ) } object S3BatchEventAttributes { - def apply(e: S3BatchEvent): List[Attribute[_]] = - List( + def apply(e: S3BatchEvent): Attributes = + Attributes( MessagingMessageId(e.invocationId), FaasTrigger(FaasTriggerValue.Datasource.value), MessagingOperation(MessagingOperationValue.Receive.value) diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventSpanAttributes.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventSpanAttributes.scala index 22ad5f6e..070a7a5a 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventSpanAttributes.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventSpanAttributes.scala @@ -16,14 +16,14 @@ package feral.lambda.otel4s -import org.typelevel.otel4s.Attribute +import org.typelevel.otel4s.Attributes import org.typelevel.otel4s.trace.SpanKind // TODO better name protected trait EventSpanAttributes[E] { def contextCarrier(e: E): Map[String, String] def spanKind: SpanKind - def attributes(e: E): List[Attribute[_]] + def attributes(e: E): Attributes } protected object EventSpanAttributes { @@ -32,7 +32,7 @@ protected object EventSpanAttributes { def contextCarrier(e: E): Map[String, String] = Map.empty def spanKind: SpanKind = sk - def attributes(e: E): List[Attribute[_]] = - List.empty + def attributes(e: E): Attributes = + Attributes.empty } } From 73ed59c65ce345751a0fa8fc59998fdd27e7a94f Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Mon, 16 Sep 2024 11:48:52 +0100 Subject: [PATCH 37/64] Remove unused import --- .../src/main/scala/feral/lambda/otel4s/EventAttributes.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala index 5f3eec48..0abbad1c 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala @@ -19,7 +19,6 @@ package feral.lambda.otel4s import feral.lambda.events.DynamoDbRecord import feral.lambda.events.S3BatchEvent import feral.lambda.events.SqsRecord -import org.typelevel.otel4s.Attribute import org.typelevel.otel4s.Attributes import LambdaMessageAttributes._ From eefbc7dd90c3166afb0c47931e3b058d6a391cfd Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Mon, 16 Sep 2024 13:05:26 +0100 Subject: [PATCH 38/64] Remove unused import --- .../src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala b/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala index 31e9c1e2..b9d251b5 100644 --- a/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala +++ b/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala @@ -25,7 +25,6 @@ import io.circe.Decoder import io.circe.Encoder import io.circe.syntax._ import munit.CatsEffectSuite -import org.typelevel.otel4s.Attribute import org.typelevel.otel4s.Attributes import org.typelevel.otel4s.sdk.testkit.trace.TracesTestkit import org.typelevel.otel4s.semconv.experimental.attributes.CloudExperimentalAttributes From b87ae6c87e73afe74da3f8e8e46f50d5277d5539 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Mon, 16 Sep 2024 15:19:22 +0100 Subject: [PATCH 39/64] Add LambdaContextAttributes test --- ...es.scala => LambdaContextAttributes.scala} | 27 +----- .../otel4s/LambdaMessageAttributes.scala | 25 +++++ .../otel4s/LambdaContextAttributesTest.scala | 92 +++++++++++++++++++ 3 files changed, 121 insertions(+), 23 deletions(-) rename lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/{Attributes.scala => LambdaContextAttributes.scala} (72%) create mode 100644 lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/LambdaMessageAttributes.scala create mode 100644 lambda-otel4s/shared/src/test/scala/feral/lambda/otel4s/LambdaContextAttributesTest.scala diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/Attributes.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/LambdaContextAttributes.scala similarity index 72% rename from lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/Attributes.scala rename to lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/LambdaContextAttributes.scala index f314fc76..4d2a04d2 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/Attributes.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/LambdaContextAttributes.scala @@ -17,29 +17,9 @@ package feral.lambda.otel4s import feral.lambda.Context -import org.typelevel.otel4s.Attribute +import org.typelevel.otel4s.Attributes import org.typelevel.otel4s.AttributeKey -/** - * Temporary aliases for Lambda message-specific attributes in otel4s-semconv-experimental - */ -private[otel4s] object LambdaMessageAttributes { - val MessagingSystem = AttributeKey.string("messaging.system") - object MessagingSystemValue { - object Sqs { - val value = "aws_sqs" - } - } - val MessagingOperation = AttributeKey.string("messaging.operation") - object MessagingOperationValue { - object Receive { - val value = "receive" - } - } - val MessagingMessageId = AttributeKey.string("messaging.message.id") - val MessagingDestinationName = AttributeKey.string("messaging.destination.name") -} - /** * Temporary aliases for Lambda platform attributes in otel4s-semconv-experimental */ @@ -57,6 +37,7 @@ private[otel4s] object LambdaContextAttributes { val value = "http" } } + // ARN val CloudResourceId = AttributeKey.string("cloud.resource_id") val FaasInstance = AttributeKey.string("faas.instance") @@ -70,8 +51,8 @@ private[otel4s] object LambdaContextAttributes { } } - def apply[F[_]](context: Context[F]): List[Attribute[_]] = { - List( + def apply[F[_]](context: Context[F]): Attributes = { + Attributes( CloudProvider(CloudProviderValue.Aws.value), CloudResourceId(context.invokedFunctionArn), FaasInstance(context.logStreamName), diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/LambdaMessageAttributes.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/LambdaMessageAttributes.scala new file mode 100644 index 00000000..2ddac1c9 --- /dev/null +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/LambdaMessageAttributes.scala @@ -0,0 +1,25 @@ +package feral.lambda.otel4s + +import feral.lambda.Context +import org.typelevel.otel4s.Attribute +import org.typelevel.otel4s.AttributeKey + +/** + * Temporary aliases for Lambda message-specific attributes in otel4s-semconv-experimental + */ +private[otel4s] object LambdaMessageAttributes { + val MessagingSystem = AttributeKey.string("messaging.system") + object MessagingSystemValue { + object Sqs { + val value = "aws_sqs" + } + } + val MessagingOperation = AttributeKey.string("messaging.operation") + object MessagingOperationValue { + object Receive { + val value = "receive" + } + } + val MessagingMessageId = AttributeKey.string("messaging.message.id") + val MessagingDestinationName = AttributeKey.string("messaging.destination.name") +} diff --git a/lambda-otel4s/shared/src/test/scala/feral/lambda/otel4s/LambdaContextAttributesTest.scala b/lambda-otel4s/shared/src/test/scala/feral/lambda/otel4s/LambdaContextAttributesTest.scala new file mode 100644 index 00000000..6c5d3572 --- /dev/null +++ b/lambda-otel4s/shared/src/test/scala/feral/lambda/otel4s/LambdaContextAttributesTest.scala @@ -0,0 +1,92 @@ +package feral.lambda.otel4s + +import munit.FunSuite +import org.typelevel.otel4s.semconv.experimental.attributes.FaasExperimentalAttributes +import org.typelevel.otel4s.semconv.experimental.attributes.CloudExperimentalAttributes + +class LambdaContextAttributesTest extends FunSuite { + + import LambdaContextAttributes._ + val otel4s = FaasExperimentalAttributes + val otel4sCloud = CloudExperimentalAttributes + + test("FaasInvocationId") { + val value = "value" + val ours = FaasInvocationId(value) + val otel = otel4s.FaasInvocationId(value) + assertEquals(ours, otel) + } + + test("FaasTrigger") { + val value = "value" + val ours = FaasTrigger(value) + val otel = otel4s.FaasTrigger(value) + assertEquals(ours, otel) + } + + test("FaasTriggerValue.PubSub") { + val ours = FaasTriggerValue.Pubsub.value + val otel = otel4s.FaasTriggerValue.Pubsub.value + assertEquals(ours, otel) + } + + test("FaasTriggerValue.Datasource") { + val ours = FaasTriggerValue.Datasource.value + val otel = otel4s.FaasTriggerValue.Datasource.value + assertEquals(ours, otel) + } + + test("FaasTriggerValue.Http") { + val ours = FaasTriggerValue.Http.value + val otel = otel4s.FaasTriggerValue.Http.value + assertEquals(ours, otel) + } + + test("CloudResourceId") { + val value = "value" + val ours = CloudResourceId(value) + val otel = otel4sCloud.CloudResourceId(value) + assertEquals(ours, otel) + } + + test("FaasInstance") { + val value = "value" + val ours = FaasInstance(value) + val otel = otel4s.FaasInstance(value) + assertEquals(ours, otel) + } + + test("FaasMaxMemory") { + val value = 0L + val ours = FaasMaxMemory(value) + val otel = otel4s.FaasMaxMemory(value) + assertEquals(ours, otel) + } + + test("FaasName") { + val value = "value" + val ours = FaasName(value) + val otel = otel4s.FaasName(value) + assertEquals(ours, otel) + } + + test("FaasVersion") { + val value = "value" + val ours = FaasVersion(value) + val otel = otel4s.FaasVersion(value) + assertEquals(ours, otel) + } + + test("CloudProvider") { + val value = "value" + val ours = CloudProvider(value) + val otel = otel4sCloud.CloudProvider(value) + assertEquals(ours, otel) + } + + test("CloudProviderValue.Aws") { + val ours = CloudProviderValue.Aws.value + val otel = otel4sCloud.CloudProviderValue.Aws.value + assertEquals(ours, otel) + } +} From 1010972edd0b58fa30b83dc10d74a9cbc28e3300 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Mon, 16 Sep 2024 15:28:42 +0100 Subject: [PATCH 40/64] Add LambdaMessageAttributesTest --- .../feral/lambda/otel4s/EventAttributes.scala | 4 +- .../otel4s/LambdaContextAttributes.scala | 2 +- .../otel4s/LambdaMessageAttributes.scala | 22 ++++-- .../otel4s/LambdaContextAttributesTest.scala | 21 +++++- .../otel4s/LambdaMessageAttributesTest.scala | 69 +++++++++++++++++++ 5 files changed, 110 insertions(+), 8 deletions(-) create mode 100644 lambda-otel4s/shared/src/test/scala/feral/lambda/otel4s/LambdaMessageAttributesTest.scala diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala index 0abbad1c..bf1f954b 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala @@ -28,7 +28,7 @@ object SqsEventAttributes { def apply(): Attributes = Attributes( FaasTrigger(FaasTriggerValue.Pubsub.value), - MessagingSystem("aws_sqs") + MessagingSystem(MessagingSystemValue.AwsSqs.value) ) } @@ -45,7 +45,7 @@ object DynamoDbStreamEventAttributes { def apply(): Attributes = Attributes( FaasTrigger(FaasTriggerValue.Datasource.value), - MessagingSystem("aws_sqs") + MessagingSystem(MessagingSystemValue.AwsSqs.value) ) } diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/LambdaContextAttributes.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/LambdaContextAttributes.scala index 4d2a04d2..96766da5 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/LambdaContextAttributes.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/LambdaContextAttributes.scala @@ -17,8 +17,8 @@ package feral.lambda.otel4s import feral.lambda.Context -import org.typelevel.otel4s.Attributes import org.typelevel.otel4s.AttributeKey +import org.typelevel.otel4s.Attributes /** * Temporary aliases for Lambda platform attributes in otel4s-semconv-experimental diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/LambdaMessageAttributes.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/LambdaMessageAttributes.scala index 2ddac1c9..d7648d84 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/LambdaMessageAttributes.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/LambdaMessageAttributes.scala @@ -1,16 +1,30 @@ +/* + * Copyright 2021 Typelevel + * + * Licensed 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 feral.lambda.otel4s -import feral.lambda.Context -import org.typelevel.otel4s.Attribute import org.typelevel.otel4s.AttributeKey /** * Temporary aliases for Lambda message-specific attributes in otel4s-semconv-experimental */ -private[otel4s] object LambdaMessageAttributes { +object LambdaMessageAttributes { val MessagingSystem = AttributeKey.string("messaging.system") object MessagingSystemValue { - object Sqs { + object AwsSqs { val value = "aws_sqs" } } diff --git a/lambda-otel4s/shared/src/test/scala/feral/lambda/otel4s/LambdaContextAttributesTest.scala b/lambda-otel4s/shared/src/test/scala/feral/lambda/otel4s/LambdaContextAttributesTest.scala index 6c5d3572..7f63554f 100644 --- a/lambda-otel4s/shared/src/test/scala/feral/lambda/otel4s/LambdaContextAttributesTest.scala +++ b/lambda-otel4s/shared/src/test/scala/feral/lambda/otel4s/LambdaContextAttributesTest.scala @@ -1,9 +1,28 @@ +/* + * Copyright 2021 Typelevel + * + * Licensed 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 feral.lambda.otel4s import munit.FunSuite -import org.typelevel.otel4s.semconv.experimental.attributes.FaasExperimentalAttributes import org.typelevel.otel4s.semconv.experimental.attributes.CloudExperimentalAttributes +import org.typelevel.otel4s.semconv.experimental.attributes.FaasExperimentalAttributes +/** + * Check attributes match experimental ones + */ class LambdaContextAttributesTest extends FunSuite { import LambdaContextAttributes._ diff --git a/lambda-otel4s/shared/src/test/scala/feral/lambda/otel4s/LambdaMessageAttributesTest.scala b/lambda-otel4s/shared/src/test/scala/feral/lambda/otel4s/LambdaMessageAttributesTest.scala new file mode 100644 index 00000000..619a10e0 --- /dev/null +++ b/lambda-otel4s/shared/src/test/scala/feral/lambda/otel4s/LambdaMessageAttributesTest.scala @@ -0,0 +1,69 @@ +/* + * Copyright 2021 Typelevel + * + * Licensed 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 feral.lambda.otel4s + +import munit.FunSuite +import org.typelevel.otel4s.semconv.experimental.attributes.MessagingExperimentalAttributes + +/** + * Check attributes match experimental ones + */ +class LambdaMessageAttributesTest extends FunSuite { + + import LambdaMessageAttributes._ + val otel4s = MessagingExperimentalAttributes + + test("MessagingSystem") { + val value = "value" + val ours = MessagingSystem(value) + val otel = otel4s.MessagingSystem(value) + assertEquals(ours, otel) + } + + test("MessagingSystemValue.AwsSqs") { + val ours = MessagingSystemValue.AwsSqs.value + val otel = otel4s.MessagingSystemValue.AwsSqs.value + assertEquals(ours, otel) + } + + test("MessagingOperation") { + val value = "value" + val ours = MessagingOperation(value) + val otel = otel4s.MessagingOperation(value) + assertEquals(ours, otel) + } + + test("MessagingOperationValue.Receive") { + val ours = MessagingOperationValue.Receive.value + val otel = otel4s.MessagingOperationValue.Receive.value + assertEquals(ours, otel) + } + + test("MessagingMessageId") { + val value = "value" + val ours = MessagingMessageId(value) + val otel = otel4s.MessagingMessageId(value) + assertEquals(ours, otel) + } + + test("MessagingDestinationName") { + val value = "value" + val ours = MessagingDestinationName(value) + val otel = otel4s.MessagingDestinationName(value) + assertEquals(ours, otel) + } +} From 92dded7d4c6ff5e0a4748ae05442e3ed60ca1655 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Mon, 16 Sep 2024 15:56:13 +0100 Subject: [PATCH 41/64] Deduplicate examples module settings keys --- build.sbt | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/build.sbt b/build.sbt index 0efc505d..a9221229 100644 --- a/build.sbt +++ b/build.sbt @@ -215,22 +215,18 @@ lazy val examples = crossProject(JSPlatform, JVMPlatform) ) ) .settings(commonSettings) + .dependsOn(lambda, lambdaHttp4s, lambdaNatchez, lambdaOtel4s, googleCloudHttp4s) .jvmSettings( libraryDependencies ++= Seq( "org.typelevel" %%% "otel4s-oteljava" % otel4sVersion, - "io.opentelemetry" % "opentelemetry-sdk-extension-autoconfigure" % "1.34.1" + "io.opentelemetry" % "opentelemetry-sdk-extension-autoconfigure" % "1.34.1", + "com.google.cloud.functions.invoker" % "java-function-invoker" % "1.3.1" )) - .dependsOn(lambda, lambdaHttp4s, lambdaNatchez, lambdaOtel4s, googleCloudHttp4s) .jsSettings( scalaJSUseMainModuleInitializer := true, Compile / mainClass := Some("feral.examples.http4sGoogleCloudHandler"), scalaJSLinkerConfig ~= { _.withModuleKind(ModuleKind.CommonJSModule) } ) - .jvmSettings( - libraryDependencies ++= Seq( - "com.google.cloud.functions.invoker" % "java-function-invoker" % "1.3.1" - ) - ) .enablePlugins(NoPublishPlugin) lazy val unidocs = project From 7687287d3ffbe8063195970a8ece2972821706f5 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Mon, 16 Sep 2024 15:56:36 +0100 Subject: [PATCH 42/64] Format build.sbt --- build.sbt | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/build.sbt b/build.sbt index a9221229..6d8b3dd0 100644 --- a/build.sbt +++ b/build.sbt @@ -216,12 +216,11 @@ lazy val examples = crossProject(JSPlatform, JVMPlatform) ) .settings(commonSettings) .dependsOn(lambda, lambdaHttp4s, lambdaNatchez, lambdaOtel4s, googleCloudHttp4s) - .jvmSettings( - libraryDependencies ++= Seq( - "org.typelevel" %%% "otel4s-oteljava" % otel4sVersion, - "io.opentelemetry" % "opentelemetry-sdk-extension-autoconfigure" % "1.34.1", - "com.google.cloud.functions.invoker" % "java-function-invoker" % "1.3.1" - )) + .jvmSettings(libraryDependencies ++= Seq( + "org.typelevel" %%% "otel4s-oteljava" % otel4sVersion, + "io.opentelemetry" % "opentelemetry-sdk-extension-autoconfigure" % "1.34.1", + "com.google.cloud.functions.invoker" % "java-function-invoker" % "1.3.1" + )) .jsSettings( scalaJSUseMainModuleInitializer := true, Compile / mainClass := Some("feral.examples.http4sGoogleCloudHandler"), From d35d8750b7a0e5ee44daef19828f401432518ba8 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Mon, 16 Sep 2024 16:10:03 +0100 Subject: [PATCH 43/64] Make otel4s attribute utilities package private --- .../scala/feral/lambda/otel4s/EventAttributeSources.scala | 2 +- .../main/scala/feral/lambda/otel4s/EventSpanAttributes.scala | 4 ++-- .../scala/feral/lambda/otel4s/LambdaMessageAttributes.scala | 2 +- .../src/main/scala/feral/lambda/otel4s/TracedHandler.scala | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributeSources.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributeSources.scala index 60732c36..6a15ccea 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributeSources.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributeSources.scala @@ -24,7 +24,7 @@ import feral.lambda.events.SqsEvent import org.typelevel.otel4s.Attributes import org.typelevel.otel4s.trace.SpanKind -trait EventAttributeSources { +private[otel4s] trait EventAttributeSources { implicit def sqsEvent: EventSpanAttributes[SqsEvent] = new EventSpanAttributes[SqsEvent] { def contextCarrier(e: SqsEvent): Map[String, String] = diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventSpanAttributes.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventSpanAttributes.scala index 070a7a5a..bad79d42 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventSpanAttributes.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventSpanAttributes.scala @@ -20,13 +20,13 @@ import org.typelevel.otel4s.Attributes import org.typelevel.otel4s.trace.SpanKind // TODO better name -protected trait EventSpanAttributes[E] { +private[otel4s] trait EventSpanAttributes[E] { def contextCarrier(e: E): Map[String, String] def spanKind: SpanKind def attributes(e: E): Attributes } -protected object EventSpanAttributes { +private[otel4s] object EventSpanAttributes { def empty[E](sk: SpanKind): EventSpanAttributes[E] = new EventSpanAttributes[E] { def contextCarrier(e: E): Map[String, String] = diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/LambdaMessageAttributes.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/LambdaMessageAttributes.scala index d7648d84..c2595e6f 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/LambdaMessageAttributes.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/LambdaMessageAttributes.scala @@ -21,7 +21,7 @@ import org.typelevel.otel4s.AttributeKey /** * Temporary aliases for Lambda message-specific attributes in otel4s-semconv-experimental */ -object LambdaMessageAttributes { +private[otel4s] object LambdaMessageAttributes { val MessagingSystem = AttributeKey.string("messaging.system") object MessagingSystemValue { object AwsSqs { diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala index 47fd6830..793499e9 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala @@ -41,7 +41,7 @@ object TracedHandler { } } yield res - def buildSpan[F[_]: Tracer, Event](event: Event, context: Context[F])( + private def buildSpan[F[_]: Tracer, Event](event: Event, context: Context[F])( implicit attr: EventSpanAttributes[Event] ): SpanOps[F] = Tracer[F] From a6f8e6cdd87cd3d5d2b2fb83f567b930e01d1a52 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Mon, 16 Sep 2024 16:10:45 +0100 Subject: [PATCH 44/64] Remove _root_ from example imports --- .../scala/feral/examples/SqsOtelJavaExample.scala | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala b/examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala index 2f79ac62..2716b7fd 100644 --- a/examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala +++ b/examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala @@ -16,14 +16,13 @@ package feral.examples -import _root_.feral.lambda.INothing -import _root_.feral.lambda.IOLambda -import _root_.feral.lambda.Invocation -import _root_.feral.lambda.events.SqsEvent -import _root_.feral.lambda.events.SqsRecord -import _root_.feral.lambda.otel4s.SqsRecordAttributes -import _root_.feral.lambda.otel4s.TracedHandler -import _root_.feral.lambda.otel4s._ +import feral.lambda.INothing +import feral.lambda.IOLambda +import feral.lambda.Invocation +import feral.lambda.events.SqsEvent +import feral.lambda.events.SqsRecord +import feral.lambda.otel4s.SqsRecordAttributes +import feral.lambda.otel4s.TracedHandler import cats.Monad import cats.effect.IO import cats.syntax.all._ From 684cb5085226528df42da3d361a49f3e326f9cf8 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Mon, 16 Sep 2024 16:38:42 +0100 Subject: [PATCH 45/64] Fix example --- .../src/main/scala/feral/examples/SqsOtelJavaExample.scala | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala b/examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala index 2716b7fd..0f40c65d 100644 --- a/examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala +++ b/examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala @@ -16,6 +16,9 @@ package feral.examples +import cats.Monad +import cats.effect.IO +import cats.syntax.all._ import feral.lambda.INothing import feral.lambda.IOLambda import feral.lambda.Invocation @@ -23,9 +26,7 @@ import feral.lambda.events.SqsEvent import feral.lambda.events.SqsRecord import feral.lambda.otel4s.SqsRecordAttributes import feral.lambda.otel4s.TracedHandler -import cats.Monad -import cats.effect.IO -import cats.syntax.all._ +import feral.lambda.otel4s._ import org.http4s.client.Client import org.http4s.ember.client.EmberClientBuilder import org.http4s.otel4s.middleware.ClientMiddleware From 353ba488aab69e0c9e9fbdd53aa5526dc5304fad Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Mon, 16 Sep 2024 16:47:07 +0100 Subject: [PATCH 46/64] Remove http4s-otel4s-middleware --- build.sbt | 3 +-- .../src/main/scala/feral/examples/SqsOtelJavaExample.scala | 6 ++++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/build.sbt b/build.sbt index 6d8b3dd0..a01665c8 100644 --- a/build.sbt +++ b/build.sbt @@ -210,8 +210,7 @@ lazy val examples = crossProject(JSPlatform, JVMPlatform) "org.http4s" %%% "http4s-ember-client" % http4sVersion, "org.tpolecat" %%% "natchez-xray" % natchezVersion, "org.tpolecat" %%% "natchez-http4s" % "0.6.0", - "org.tpolecat" %%% "skunk-core" % "0.6.3", - "org.http4s" %%% "http4s-otel4s-middleware" % "0.8.0" + "org.tpolecat" %%% "skunk-core" % "0.6.3" ) ) .settings(commonSettings) diff --git a/examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala b/examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala index 0f40c65d..ab425d54 100644 --- a/examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala +++ b/examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala @@ -29,19 +29,21 @@ import feral.lambda.otel4s.TracedHandler import feral.lambda.otel4s._ import org.http4s.client.Client import org.http4s.ember.client.EmberClientBuilder -import org.http4s.otel4s.middleware.ClientMiddleware import org.typelevel.otel4s.oteljava.OtelJava import org.typelevel.otel4s.trace.Tracer import org.typelevel.scalaccompat.annotation.unused object SqsOtelExample extends IOLambda[SqsEvent, INothing] { + // Could be http4s-otel4s-middleware ClientMiddleware for example + def clientMiddleware(client: Client[IO]): Client[IO] = client + def handler = OtelJava.autoConfigured[IO]().map(_.tracerProvider).evalMap(_.get("tracer")).flatMap { implicit tracer: Tracer[IO] => for { client <- EmberClientBuilder.default[IO].build - tracedClient = ClientMiddleware.default[IO].build(client) + tracedClient = clientMiddleware(client) } yield { implicit inv: Invocation[IO, SqsEvent] => TracedHandler[IO, SqsEvent, INothing]( handleEvent[IO](tracedClient) From 07d2bbe585c93a84a589d8de359087125db74c09 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Mon, 16 Sep 2024 16:47:22 +0100 Subject: [PATCH 47/64] Bump otel to 0.9 --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index a01665c8..8ead5005 100644 --- a/build.sbt +++ b/build.sbt @@ -58,7 +58,7 @@ val natchezVersion = "0.3.6" val munitVersion = "0.7.29" val munitCEVersion = "1.0.7" val scalacheckEffectVersion = "1.0.4" -val otel4sVersion = "0.8.0" +val otel4sVersion = "0.9.0" lazy val commonSettings = Seq( crossScalaVersions := Seq(Scala3, Scala213) From 20d7b5f378f1c7af3e01e6beaafb9b1a278ccb63 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Mon, 16 Sep 2024 19:28:48 +0100 Subject: [PATCH 48/64] Fix 0.9 bump --- .../scala/feral/lambda/otel4s/TracedHandlerSuite.scala | 4 +++- .../scala/feral/lambda/otel4s/TracedHandlerSuite.scala | 4 ++-- .../scala/feral/lambda/otel4s/EventAttributes.scala | 8 ++++---- .../feral/lambda/otel4s/LambdaMessageAttributes.scala | 4 ++-- .../lambda/otel4s/LambdaMessageAttributesTest.scala | 10 +++++----- 5 files changed, 16 insertions(+), 14 deletions(-) diff --git a/lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala b/lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala index f98d7009..619acd68 100644 --- a/lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala +++ b/lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala @@ -73,7 +73,9 @@ class TracedHandlerSuite extends CatsEffectSuite { _ <- IO { assertEquals(spans.length, 1) assertEquals(spans.headOption.map(_.name), Some(functionName)) - assertEquals(spans.headOption.map(_.attributes.map(_.key).toSet), Some(attributeKeys)) + assertEquals( + spans.headOption.map(_.attributes.elements.map(_.key).toSet), + Some(attributeKeys)) assertEquals(allocationCounter.get(), 1) assertEquals(invokeCounter.get(), 1) assertEquals(res, event.asInstanceOf[js.UndefOr[js.Any]]) diff --git a/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala b/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala index b9d251b5..a26a7395 100644 --- a/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala +++ b/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala @@ -100,7 +100,7 @@ class TracedHandlerSuite extends CatsEffectSuite { assertEquals(res, "\"body\"") assertEquals(spans.length, 1) assertEquals(spans.headOption.map(_.name), Some(functionName)) - assertEquals(spans.headOption.map(_.attributes), Some(attributes)) + assertEquals(spans.headOption.map(_.attributes.elements), Some(attributes)) assertEquals(allocationCounter.get(), 1) assertEquals(invokeCounter.get(), 1) } @@ -156,7 +156,7 @@ class TracedHandlerSuite extends CatsEffectSuite { assertEquals(res.length, chars.length) assertEquals(spans.length, chars.length) assertEquals(spans.map(_.name), expectedSpanNames) - assertEquals(spans.map(_.attributes), expectedAttributes) + assertEquals(spans.map(_.attributes.elements), expectedAttributes) } } yield () } diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala index bf1f954b..b96a007a 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributes.scala @@ -36,7 +36,7 @@ object SqsRecordAttributes { def apply(e: SqsRecord): Attributes = Attributes( FaasTrigger(FaasTriggerValue.Pubsub.value), - MessagingOperation(MessagingOperationValue.Receive.value), + MessagingOperationType(MessagingOperationTypeValue.Receive.value), MessagingMessageId(e.messageId) ) } @@ -53,7 +53,7 @@ object DynamoDbRecordAttributes { def apply(e: DynamoDbRecord): Attributes = { Attributes( FaasTrigger(FaasTriggerValue.Datasource.value), - MessagingOperation(MessagingOperationValue.Receive.value) + MessagingOperationType(MessagingOperationTypeValue.Receive.value) ) ++ e.eventId.map(MessagingMessageId(_)) } } @@ -62,7 +62,7 @@ object ApiGatewayProxyEventAttributes { def apply(): Attributes = Attributes( FaasTrigger(FaasTriggerValue.Http.value), - MessagingOperation(MessagingOperationValue.Receive.value) + MessagingOperationType(MessagingOperationTypeValue.Receive.value) ) } @@ -71,6 +71,6 @@ object S3BatchEventAttributes { Attributes( MessagingMessageId(e.invocationId), FaasTrigger(FaasTriggerValue.Datasource.value), - MessagingOperation(MessagingOperationValue.Receive.value) + MessagingOperationType(MessagingOperationTypeValue.Receive.value) ) } diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/LambdaMessageAttributes.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/LambdaMessageAttributes.scala index c2595e6f..975407e5 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/LambdaMessageAttributes.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/LambdaMessageAttributes.scala @@ -28,8 +28,8 @@ private[otel4s] object LambdaMessageAttributes { val value = "aws_sqs" } } - val MessagingOperation = AttributeKey.string("messaging.operation") - object MessagingOperationValue { + val MessagingOperationType = AttributeKey.string("messaging.operation.type") + object MessagingOperationTypeValue { object Receive { val value = "receive" } diff --git a/lambda-otel4s/shared/src/test/scala/feral/lambda/otel4s/LambdaMessageAttributesTest.scala b/lambda-otel4s/shared/src/test/scala/feral/lambda/otel4s/LambdaMessageAttributesTest.scala index 619a10e0..57c9fea8 100644 --- a/lambda-otel4s/shared/src/test/scala/feral/lambda/otel4s/LambdaMessageAttributesTest.scala +++ b/lambda-otel4s/shared/src/test/scala/feral/lambda/otel4s/LambdaMessageAttributesTest.scala @@ -40,16 +40,16 @@ class LambdaMessageAttributesTest extends FunSuite { assertEquals(ours, otel) } - test("MessagingOperation") { + test("MessagingOperationType") { val value = "value" - val ours = MessagingOperation(value) - val otel = otel4s.MessagingOperation(value) + val ours = MessagingOperationType(value) + val otel = otel4s.MessagingOperationType(value) assertEquals(ours, otel) } test("MessagingOperationValue.Receive") { - val ours = MessagingOperationValue.Receive.value - val otel = otel4s.MessagingOperationValue.Receive.value + val ours = MessagingOperationTypeValue.Receive.value + val otel = otel4s.MessagingOperationTypeValue.Receive.value assertEquals(ours, otel) } From c52d9018b9ef908b32f29e72f773fd9f727702d2 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Wed, 25 Sep 2024 09:53:31 +0100 Subject: [PATCH 49/64] Bump otel4s to 0.10 --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 8ead5005..06ffa098 100644 --- a/build.sbt +++ b/build.sbt @@ -58,7 +58,7 @@ val natchezVersion = "0.3.6" val munitVersion = "0.7.29" val munitCEVersion = "1.0.7" val scalacheckEffectVersion = "1.0.4" -val otel4sVersion = "0.9.0" +val otel4sVersion = "0.10.0" lazy val commonSettings = Seq( crossScalaVersions := Seq(Scala3, Scala213) From c0625519414a7fed651ddbe75b7554d8c0375344 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Thu, 26 Sep 2024 10:13:04 +0100 Subject: [PATCH 50/64] Move KernelSources to KernelSource companion --- .../feral/lambda/natchez/KernelSource.scala | 30 +++++++++++ .../scala/feral/lambda/natchez/package.scala | 54 ------------------- 2 files changed, 30 insertions(+), 54 deletions(-) delete mode 100644 lambda-natchez/shared/src/main/scala/feral/lambda/natchez/package.scala diff --git a/lambda-natchez/shared/src/main/scala/feral/lambda/natchez/KernelSource.scala b/lambda-natchez/shared/src/main/scala/feral/lambda/natchez/KernelSource.scala index 4188ef9a..8bf40981 100644 --- a/lambda-natchez/shared/src/main/scala/feral/lambda/natchez/KernelSource.scala +++ b/lambda-natchez/shared/src/main/scala/feral/lambda/natchez/KernelSource.scala @@ -16,7 +16,14 @@ package feral.lambda.natchez +import feral.lambda.events.ApiGatewayProxyEvent +import feral.lambda.events.ApiGatewayProxyEventV2 +import feral.lambda.events.DynamoDbStreamEvent +import feral.lambda.events.KinesisStreamEvent +import feral.lambda.events.S3BatchEvent +import feral.lambda.events.SqsRecordAttributes import natchez.Kernel +import org.typelevel.ci._ trait KernelSource[Event] { def extract(event: Event): Kernel @@ -26,4 +33,27 @@ object KernelSource { @inline def apply[E](implicit ev: KernelSource[E]): ev.type = ev def emptyKernelSource[E]: KernelSource[E] = _ => Kernel(Map.empty) + + private[this] val `X-Amzn-Trace-Id` = ci"X-Amzn-Trace-Id" + + implicit def apiGatewayProxyEvent: KernelSource[ApiGatewayProxyEvent] = + e => Kernel(e.headers.getOrElse(Map.empty)) + + implicit def apiGatewayProxyEventV2: KernelSource[ApiGatewayProxyEventV2] = + e => Kernel(e.headers) + + implicit def sqsRecordAttributes: KernelSource[SqsRecordAttributes] = + a => Kernel(a.awsTraceHeader.map(`X-Amzn-Trace-Id` -> _).toMap) + + implicit def s3BatchEvent: KernelSource[S3BatchEvent] = KernelSource.emptyKernelSource + + @deprecated( + "See feral.lambda.events.KinesisStreamEvent deprecation", + since = "0.3.0" + ) + implicit def kinesisStreamEvent: KernelSource[KinesisStreamEvent] = + KernelSource.emptyKernelSource + + implicit def dynamoDbStreamEvent: KernelSource[DynamoDbStreamEvent] = + KernelSource.emptyKernelSource } diff --git a/lambda-natchez/shared/src/main/scala/feral/lambda/natchez/package.scala b/lambda-natchez/shared/src/main/scala/feral/lambda/natchez/package.scala deleted file mode 100644 index c8ecd516..00000000 --- a/lambda-natchez/shared/src/main/scala/feral/lambda/natchez/package.scala +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2021 Typelevel - * - * Licensed 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 feral.lambda - -import _root_.natchez.Kernel -import feral.lambda.events.ApiGatewayProxyEvent -import feral.lambda.events.ApiGatewayProxyEventV2 -import feral.lambda.events.DynamoDbStreamEvent -import feral.lambda.events.KinesisStreamEvent -import feral.lambda.events.S3BatchEvent -import feral.lambda.events.SqsRecordAttributes -import feral.lambda.natchez.KernelSource -import org.typelevel.ci._ - -protected trait KernelSources { - private[this] val `X-Amzn-Trace-Id` = ci"X-Amzn-Trace-Id" - - implicit def apiGatewayProxyEvent: KernelSource[ApiGatewayProxyEvent] = - e => Kernel(e.headers.getOrElse(Map.empty)) - - implicit def apiGatewayProxyEventV2: KernelSource[ApiGatewayProxyEventV2] = - e => Kernel(e.headers) - - implicit def sqsRecordAttributes: KernelSource[SqsRecordAttributes] = - a => Kernel(a.awsTraceHeader.map(`X-Amzn-Trace-Id` -> _).toMap) - - implicit def s3BatchEvent: KernelSource[S3BatchEvent] = KernelSource.emptyKernelSource - - @deprecated( - "See feral.lambda.events.KinesisStreamEvent deprecation", - since = "0.3.0" - ) - implicit def kinesisStreamEvent: KernelSource[KinesisStreamEvent] = - KernelSource.emptyKernelSource - - implicit def dynamoDbStreamEvent: KernelSource[DynamoDbStreamEvent] = - KernelSource.emptyKernelSource -} - -package object natchez extends KernelSources From 8ebf71c84f98c74ef3b27acb292f07ad6deb01ef Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Sun, 29 Sep 2024 11:31:48 +0100 Subject: [PATCH 51/64] Bump skunk again --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 06ffa098..5e9e16b7 100644 --- a/build.sbt +++ b/build.sbt @@ -210,7 +210,7 @@ lazy val examples = crossProject(JSPlatform, JVMPlatform) "org.http4s" %%% "http4s-ember-client" % http4sVersion, "org.tpolecat" %%% "natchez-xray" % natchezVersion, "org.tpolecat" %%% "natchez-http4s" % "0.6.0", - "org.tpolecat" %%% "skunk-core" % "0.6.3" + "org.tpolecat" %%% "skunk-core" % "0.6.4" ) ) .settings(commonSettings) From e538c3f9b57a59a1fcf914a4f02e470da16cb783 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Sun, 29 Sep 2024 11:43:38 +0100 Subject: [PATCH 52/64] Remove Event parameter from TracedHandler --- .../scala/feral/examples/SqsOtelJavaExample.scala | 2 +- .../feral/lambda/otel4s/TracedHandlerSuite.scala | 6 +++--- .../feral/lambda/otel4s/TracedHandlerSuite.scala | 12 ++++++------ .../scala/feral/lambda/otel4s/TracedHandler.scala | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala b/examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala index ab425d54..9059893b 100644 --- a/examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala +++ b/examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala @@ -53,7 +53,7 @@ object SqsOtelExample extends IOLambda[SqsEvent, INothing] { def handleEvent[F[_]: Monad: Tracer]( @unused client: Client[F] - ): SqsEvent => F[Option[INothing]] = { event => + )(implicit inv: Invocation[F, SqsEvent]): F[Option[INothing]] = inv.event.flatMap { event => event .records .traverse(record => diff --git a/lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala b/lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala index 619acd68..557e539e 100644 --- a/lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala +++ b/lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala @@ -43,13 +43,13 @@ class TracedHandlerSuite extends CatsEffectSuite { val lambda = new IOLambda[String, String] { def handler = Resource.eval(IO(allocationCounter.getAndIncrement())).as { implicit inv => - def fn(ev: String): IO[Option[String]] = + TracedHandler { for { _ <- IO(invokeCounter.getAndIncrement()) + ev <- inv.event res = Some(ev) } yield res - - TracedHandler(fn) + } } } diff --git a/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala b/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala index a26a7395..357ab2bf 100644 --- a/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala +++ b/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala @@ -68,13 +68,13 @@ class TracedHandlerSuite extends CatsEffectSuite { val lambda = new IOLambda[TestEvent, String] { def handler = Resource.eval(IO(allocationCounter.getAndIncrement())).as { implicit inv => - def fn(ev: TestEvent): IO[Option[String]] = + TracedHandler { for { _ <- IO(invokeCounter.getAndIncrement()) + ev <- inv.event res = Some(ev.payload) } yield res - - TracedHandler(fn) + } } } @@ -117,13 +117,13 @@ class TracedHandlerSuite extends CatsEffectSuite { val lambda = new IOLambda[TestEvent, String] { def handler = Resource.eval(IO(allocationCounter.getAndIncrement())).as { implicit inv => - def fn(ev: TestEvent): IO[Option[String]] = + TracedHandler { for { _ <- IO(invokeCounter.getAndIncrement()) + ev <- inv.event res = Some(ev.payload) } yield res - - TracedHandler(fn) + } } } diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala index 793499e9..d2cc9724 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala @@ -26,7 +26,7 @@ import org.typelevel.otel4s.trace.Tracer object TracedHandler { def apply[F[_]: Monad: Tracer, Event, Result]( - handler: Event => F[Option[Result]] + handler: F[Option[Result]] )( implicit inv: Invocation[F, Event], attr: EventSpanAttributes[Event] @@ -36,7 +36,7 @@ object TracedHandler { context <- inv.context res <- Tracer[F].joinOrRoot(attr.contextCarrier(event)) { buildSpan(event, context).surround { - handler(event) + handler } } } yield res From 4231bca4fd1e51507ebcc443ae3dcef1d513d230 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Sun, 29 Sep 2024 16:06:43 +0100 Subject: [PATCH 53/64] Use shared trace handler suite --- .../lambda/otel4s/TracedHandlerSuite.scala | 107 --------- .../lambda/otel4s/TracedHandlerSuite.scala | 205 ------------------ .../lambda/otel4s/TracedHandlerSuite.scala | 166 ++++++++++++++ 3 files changed, 166 insertions(+), 312 deletions(-) delete mode 100644 lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala delete mode 100644 lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala create mode 100644 lambda-otel4s/shared/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala diff --git a/lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala b/lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala deleted file mode 100644 index 557e539e..00000000 --- a/lambda-otel4s/js/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2021 Typelevel - * - * Licensed 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 feral.lambda -package otel4s - -import cats.effect.IO -import cats.effect.kernel.Resource -import cats.syntax.all._ -import munit.CatsEffectSuite -import org.typelevel.otel4s.AttributeKey -import org.typelevel.otel4s.sdk.testkit.trace.TracesTestkit -import org.typelevel.otel4s.semconv.experimental.attributes.CloudExperimentalAttributes -import org.typelevel.otel4s.semconv.experimental.attributes.FaasExperimentalAttributes -import org.typelevel.otel4s.trace.SpanKind - -import java.util.concurrent.atomic.AtomicInteger -import scala.scalajs.js - -class TracedHandlerSuite extends CatsEffectSuite { - import TracedHandlerSuite._ - - val fixture = ResourceFixture(TracesTestkit.inMemory[IO]()) - - fixture.test("single root span is created for single invocation") { traces => - traces.tracerProvider.tracer("test-tracer").get.flatMap { implicit tracer => - val allocationCounter = new AtomicInteger - val invokeCounter = new AtomicInteger - - val lambda = new IOLambda[String, String] { - def handler = - Resource.eval(IO(allocationCounter.getAndIncrement())).as { implicit inv => - TracedHandler { - for { - _ <- IO(invokeCounter.getAndIncrement()) - ev <- inv.event - res = Some(ev) - } yield res - } - } - } - - val event = "event" - - val functionName = "test-function-name" - val run = IO.fromPromise(IO(lambda.handlerFn(event, new DummyContext(functionName)))) - - val attributeKeys = Set[AttributeKey[_]]( - FaasExperimentalAttributes.FaasName, - FaasExperimentalAttributes.FaasVersion, - FaasExperimentalAttributes.FaasInstance, - FaasExperimentalAttributes.FaasMaxMemory, - CloudExperimentalAttributes.CloudProvider, - CloudExperimentalAttributes.CloudResourceId - ) - - for { - res <- run - spans <- traces.finishedSpans - _ <- IO { - assertEquals(spans.length, 1) - assertEquals(spans.headOption.map(_.name), Some(functionName)) - assertEquals( - spans.headOption.map(_.attributes.elements.map(_.key).toSet), - Some(attributeKeys)) - assertEquals(allocationCounter.get(), 1) - assertEquals(invokeCounter.get(), 1) - assertEquals(res, event.asInstanceOf[js.UndefOr[js.Any]]) - } - } yield () - } - } - - class DummyContext(fnName: String) extends facade.Context { - def functionName = fnName - def functionVersion = "" - def invokedFunctionArn = "" - def memoryLimitInMB = "0" - def awsRequestId = "" - def logGroupName = "" - def logStreamName = "" - def identity = js.undefined - def clientContext = js.undefined - def getRemainingTimeInMillis(): Double = 0 - } - -} - -object TracedHandlerSuite { - - implicit val attr: EventSpanAttributes[String] = - EventSpanAttributes.empty[String](SpanKind.Internal) - -} diff --git a/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala b/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala deleted file mode 100644 index 357ab2bf..00000000 --- a/lambda-otel4s/jvm/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright 2021 Typelevel - * - * Licensed 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 feral.lambda -package otel4s - -import cats.effect.IO -import cats.effect.kernel.Resource -import cats.syntax.all._ -import com.amazonaws.services.lambda.runtime -import io.circe.Decoder -import io.circe.Encoder -import io.circe.syntax._ -import munit.CatsEffectSuite -import org.typelevel.otel4s.Attributes -import org.typelevel.otel4s.sdk.testkit.trace.TracesTestkit -import org.typelevel.otel4s.semconv.experimental.attributes.CloudExperimentalAttributes -import org.typelevel.otel4s.semconv.experimental.attributes.CloudExperimentalAttributes.CloudProviderValue -import org.typelevel.otel4s.semconv.experimental.attributes.FaasExperimentalAttributes -import org.typelevel.otel4s.trace.SpanKind - -import java.io.ByteArrayInputStream -import java.io.ByteArrayOutputStream -import java.util.concurrent.atomic.AtomicInteger - -class TracedHandlerSuite extends CatsEffectSuite { - import TracedHandlerSuite._ - - implicit class HandleOps[A, B](lambda: IOLambda[A, B]) { - def handleRequestHelper(in: String, ctx: runtime.Context): String = { - val os = new ByteArrayOutputStream - lambda.handleRequest( - new ByteArrayInputStream(in.getBytes()), - os, - ctx - ) - new String(os.toByteArray()) - } - } - - val fixture = ResourceFixture(TracesTestkit.inMemory[IO]()) - - private val memoryLimitInMB = 1024 - private val functionVersion = "1.0.1" - private val logStreamNameArn = - "arn:aws:logs:us-east-1:123456789012:log-group:/aws/lambda/my-function:*" - private val functionArn = - "arn:aws:lambda:us-west-2:123456789012:function:test-function-name:PROD" - - fixture.test("single root span is created for single invocation") { traces => - traces.tracerProvider.tracer("test-tracer").get.flatMap { implicit tracer => - val allocationCounter = new AtomicInteger - val invokeCounter = new AtomicInteger - - val lambda = new IOLambda[TestEvent, String] { - def handler = - Resource.eval(IO(allocationCounter.getAndIncrement())).as { implicit inv => - TracedHandler { - for { - _ <- IO(invokeCounter.getAndIncrement()) - ev <- inv.event - res = Some(ev.payload) - } yield res - } - } - } - - val event = TestEvent("1", "body") - val payload = event.asJson.noSpaces - - val functionName = "test-function-name" - val run = IO(lambda.handleRequestHelper(payload, DummyContext(functionName))) - - val attributes = Attributes( - FaasExperimentalAttributes.FaasName(functionName), - FaasExperimentalAttributes.FaasVersion(functionVersion), - FaasExperimentalAttributes.FaasInstance(logStreamNameArn), - FaasExperimentalAttributes.FaasMaxMemory(memoryLimitInMB * 1024L * 1024L), - CloudExperimentalAttributes.CloudProvider(CloudProviderValue.Aws.value), - CloudExperimentalAttributes.CloudResourceId(functionArn) - ) - - for { - res <- run - spans <- traces.finishedSpans - _ <- IO { - assertEquals(res, "\"body\"") - assertEquals(spans.length, 1) - assertEquals(spans.headOption.map(_.name), Some(functionName)) - assertEquals(spans.headOption.map(_.attributes.elements), Some(attributes)) - assertEquals(allocationCounter.get(), 1) - assertEquals(invokeCounter.get(), 1) - } - } yield () - } - - } - - fixture.test("multiple root span per invocation created with function name ") { traces => - traces.tracerProvider.tracer("test-tracer").get.flatMap { implicit tracer => - val allocationCounter = new AtomicInteger - val invokeCounter = new AtomicInteger - - val lambda = new IOLambda[TestEvent, String] { - def handler = - Resource.eval(IO(allocationCounter.getAndIncrement())).as { implicit inv => - TracedHandler { - for { - _ <- IO(invokeCounter.getAndIncrement()) - ev <- inv.event - res = Some(ev.payload) - } yield res - } - } - } - - val functionName = "test-function-name" - val chars = 'A'.to('C').toList - val run = - chars.zipWithIndex.map { case (c, i) => TestEvent(i.toString, c.toString) }.traverse { - e => - val payload = e.asJson.noSpaces - IO(lambda.handleRequestHelper(payload, DummyContext(functionName))) - } - - val expectedSpanNames = List.fill(3)(functionName) - - val expectedAttributes = List.fill(3)( - Attributes( - FaasExperimentalAttributes.FaasName(functionName), - FaasExperimentalAttributes.FaasVersion(functionVersion), - FaasExperimentalAttributes.FaasInstance(logStreamNameArn), - FaasExperimentalAttributes.FaasMaxMemory(memoryLimitInMB * 1024L * 1024L), - CloudExperimentalAttributes.CloudProvider(CloudProviderValue.Aws.value), - CloudExperimentalAttributes.CloudResourceId(functionArn) - ) - ) - - for { - res <- run - spans <- traces.finishedSpans - _ <- IO { - assertEquals(res.length, chars.length) - assertEquals(spans.length, chars.length) - assertEquals(spans.map(_.name), expectedSpanNames) - assertEquals(spans.map(_.attributes.elements), expectedAttributes) - } - } yield () - } - } - - case class DummyContext(functionName: String) extends runtime.Context { - override def getAwsRequestId(): String = "" - override def getLogGroupName(): String = "" - override def getLogStreamName(): String = logStreamNameArn - override def getFunctionName(): String = functionName - override def getFunctionVersion(): String = functionVersion - override def getInvokedFunctionArn(): String = functionArn - override def getIdentity(): runtime.CognitoIdentity = null - override def getClientContext(): runtime.ClientContext = null - override def getRemainingTimeInMillis(): Int = Int.MaxValue - override def getMemoryLimitInMB(): Int = memoryLimitInMB - override def getLogger(): runtime.LambdaLogger = null - } - -} - -object TracedHandlerSuite { - - case class TestEvent(traceId: String, payload: String) - - object TestEvent { - - implicit val decoder: Decoder[TestEvent] = - Decoder.forProduct2("traceId", "payload")(TestEvent.apply) - implicit val encoder: Encoder[TestEvent] = - Encoder.forProduct2("traceId", "payload")(ev => (ev.traceId, ev.payload)) - - implicit val attr: EventSpanAttributes[TestEvent] = - new EventSpanAttributes[TestEvent] { - - override def contextCarrier(e: TestEvent): Map[String, String] = - Map("trace_id" -> e.traceId) - - override def spanKind: SpanKind = SpanKind.Consumer - - override def attributes(e: TestEvent): Attributes = Attributes.empty - - } - } - -} diff --git a/lambda-otel4s/shared/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala b/lambda-otel4s/shared/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala new file mode 100644 index 00000000..ac136c18 --- /dev/null +++ b/lambda-otel4s/shared/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala @@ -0,0 +1,166 @@ +/* + * Copyright 2021 Typelevel + * + * Licensed 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 feral.lambda +package otel4s + +import cats.effect.IO +import cats.syntax.all._ +import io.circe.Decoder +import io.circe.Encoder +import munit.CatsEffectSuite +import org.typelevel.otel4s.Attributes +import org.typelevel.otel4s.sdk.testkit.trace.TracesTestkit +import org.typelevel.otel4s.semconv.experimental.attributes.CloudExperimentalAttributes +import org.typelevel.otel4s.semconv.experimental.attributes.CloudExperimentalAttributes.CloudProviderValue +import org.typelevel.otel4s.semconv.experimental.attributes.FaasExperimentalAttributes +import org.typelevel.otel4s.trace.SpanKind + +import java.util.concurrent.atomic.AtomicInteger + +class SharedTracedHandlerSuite extends CatsEffectSuite { + import SharedTracedHandlerSuite._ + + val fixture = ResourceFixture(TracesTestkit.inMemory[IO]()) + + fixture.test("single root span is created for single invocation") { traces => + traces.tracerProvider.tracer("test-tracer").get.flatMap { implicit tracer => + val event = TestEvent("1", "body") + + val attributes = Attributes( + FaasExperimentalAttributes.FaasName(functionName), + FaasExperimentalAttributes.FaasVersion(functionVersion), + FaasExperimentalAttributes.FaasInstance(logStreamName), + FaasExperimentalAttributes.FaasMaxMemory(memoryLimitInMB * 1024L * 1024L), + CloudExperimentalAttributes.CloudProvider(CloudProviderValue.Aws.value), + CloudExperimentalAttributes.CloudResourceId(invokedFunctionArn) + ) + + implicit val inv: Invocation[IO, TestEvent] = makeInvocation(event) + + val invokeCounter = new AtomicInteger + + val tracedHandler = TracedHandler[IO, TestEvent, INothing] { + for { + _ <- IO(invokeCounter.getAndIncrement()) + } yield Option.empty[INothing] + } + + for { + _ <- tracedHandler + spans <- traces.finishedSpans + headSpan = spans.headOption + } yield { + assertEquals(spans.length, 1) + assertEquals(headSpan.map(_.name), Some(functionName)) + assertEquals(headSpan.map(_.attributes.elements), Some(attributes)) + assertEquals(invokeCounter.get(), 1) + } + + } + } + + fixture.test("multiple root spans created for multiple invocations") { traces => + traces.tracerProvider.tracer("test-tracer").get.flatMap { implicit tracer => + val event = TestEvent("2", "body") + + val attributes = Attributes( + FaasExperimentalAttributes.FaasName(functionName), + FaasExperimentalAttributes.FaasVersion(functionVersion), + FaasExperimentalAttributes.FaasInstance(logStreamName), + FaasExperimentalAttributes.FaasMaxMemory(memoryLimitInMB * 1024L * 1024L), + CloudExperimentalAttributes.CloudProvider(CloudProviderValue.Aws.value), + CloudExperimentalAttributes.CloudResourceId(invokedFunctionArn) + ) + + implicit val inv: Invocation[IO, TestEvent] = makeInvocation(event) + + val invokeCounter = new AtomicInteger + + val tracedHandler = TracedHandler[IO, TestEvent, INothing] { + for { + _ <- IO(invokeCounter.getAndIncrement()) + } yield Option.empty[INothing] + } + + val handlerIndices = 0.to(4) + val runHandlers = handlerIndices.toList.map(_ => tracedHandler).sequence + + for { + _ <- runHandlers + spans <- traces.finishedSpans + } yield { + assertEquals(spans.length, handlerIndices.size) + spans.map(s => assertEquals(s.name, functionName)) + spans.map(s => assertEquals(s.attributes.elements, attributes)) + assertEquals(invokeCounter.get(), handlerIndices.size) + } + } + } +} + +object SharedTracedHandlerSuite { + + case class TestEvent(traceId: String, payload: String) + + object TestEvent { + + implicit val decoder: Decoder[TestEvent] = + Decoder.forProduct2("traceId", "payload")(TestEvent.apply) + implicit val encoder: Encoder[TestEvent] = + Encoder.forProduct2("traceId", "payload")(ev => (ev.traceId, ev.payload)) + + implicit val attr: EventSpanAttributes[TestEvent] = + new EventSpanAttributes[TestEvent] { + + override def contextCarrier(e: TestEvent): Map[String, String] = + Map("trace_id" -> e.traceId) + + override def spanKind: SpanKind = SpanKind.Consumer + + override def attributes(e: TestEvent): Attributes = Attributes.empty + + } + } + + val memoryLimitInMB = 1024 + val functionVersion = "1.0.1" + val logStreamNameArn = + "arn:aws:logs:us-east-1:123456789012:log-group:/aws/lambda/my-function:*" + val invokedFunctionArn = + "arn:aws:lambda:us-west-2:123456789012:function:test-function-name:PROD" + val functionName = "test-function-name" + val logStreamName = "log-stream-name" + val logGroupName = "log-group-name" + + val context: Context[IO] = { + Context.apply[IO]( + functionName = functionName, + functionVersion = functionVersion, + invokedFunctionArn = invokedFunctionArn, + memoryLimitInMB = memoryLimitInMB, + awsRequestId = "aws-request-id", + logGroupName = logGroupName, + logStreamName = logStreamName, + identity = None, + clientContext = None, + remainingTime = IO.realTime + ) + } + + def makeInvocation(event: TestEvent): Invocation[IO, TestEvent] = + Invocation.pure[IO, TestEvent](event, context) +} From 29edc993b1a39225a3cf04d545366d871bd6c927 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Sun, 29 Sep 2024 16:22:40 +0100 Subject: [PATCH 54/64] Add v0.4.0 rewrites for natchez module --- .../scala/feral/scalafix/V0_4_0Rewrites.scala | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 scalafix/rules/src/main/scala/feral/scalafix/V0_4_0Rewrites.scala diff --git a/scalafix/rules/src/main/scala/feral/scalafix/V0_4_0Rewrites.scala b/scalafix/rules/src/main/scala/feral/scalafix/V0_4_0Rewrites.scala new file mode 100644 index 00000000..4d66d1d3 --- /dev/null +++ b/scalafix/rules/src/main/scala/feral/scalafix/V0_4_0Rewrites.scala @@ -0,0 +1,28 @@ +/* + * Copyright 2023 Typelevel + * + * Licensed 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 feral.scalafix + +import scalafix.v1._ + +class V0_4_0Rewrites extends SemanticRule("V0_4_0Rewrites") { + override def fix(implicit doc: SemanticDocument): Patch = + Patch.replaceSymbols( + "feral.lambda.AwsTags" -> "feral.lambda.natchez.AwsTags", + "feral.lambda.KernelSource" -> "feral.lambda.natchez.KernelSource", + "feral.lambda.TracedHandler" -> "feral.lambda.natchez.TracedHandler", + ) +} From 9bcbb1323c1d387cf1d1f83d715e288eaacf80e1 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Sun, 29 Sep 2024 16:24:25 +0100 Subject: [PATCH 55/64] Fix formatting --- .../rules/src/main/scala/feral/scalafix/V0_4_0Rewrites.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scalafix/rules/src/main/scala/feral/scalafix/V0_4_0Rewrites.scala b/scalafix/rules/src/main/scala/feral/scalafix/V0_4_0Rewrites.scala index 4d66d1d3..683673aa 100644 --- a/scalafix/rules/src/main/scala/feral/scalafix/V0_4_0Rewrites.scala +++ b/scalafix/rules/src/main/scala/feral/scalafix/V0_4_0Rewrites.scala @@ -23,6 +23,6 @@ class V0_4_0Rewrites extends SemanticRule("V0_4_0Rewrites") { Patch.replaceSymbols( "feral.lambda.AwsTags" -> "feral.lambda.natchez.AwsTags", "feral.lambda.KernelSource" -> "feral.lambda.natchez.KernelSource", - "feral.lambda.TracedHandler" -> "feral.lambda.natchez.TracedHandler", + "feral.lambda.TracedHandler" -> "feral.lambda.natchez.TracedHandler" ) } From ef7d8836800e8da07dc030d2c5222a1061b96f5e Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Sun, 29 Sep 2024 16:31:02 +0100 Subject: [PATCH 56/64] Add scalafix v0.4.0 rewrite input/output --- .../src/main/scala/example/V0_4_0Rewrites.scala | 17 +++++++++++++++++ .../src/main/scala/example/V0_4_0Rewrites.scala | 13 +++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 scalafix/input/src/main/scala/example/V0_4_0Rewrites.scala create mode 100644 scalafix/output/src/main/scala/example/V0_4_0Rewrites.scala diff --git a/scalafix/input/src/main/scala/example/V0_4_0Rewrites.scala b/scalafix/input/src/main/scala/example/V0_4_0Rewrites.scala new file mode 100644 index 00000000..31b27aae --- /dev/null +++ b/scalafix/input/src/main/scala/example/V0_4_0Rewrites.scala @@ -0,0 +1,17 @@ +/* rule=V0_4_0Rewrites */ + +package example + +// format: off +import feral.lambda.AwsTags +import feral.lambda.KernelSource +import feral.lambda.TracedHandler +// format: on + +object V0_4_0RewritesInput { + + val tags: AwsTags = ??? + val source: KernelSource = ??? + val handler: TracedHandler = ??? + +} diff --git a/scalafix/output/src/main/scala/example/V0_4_0Rewrites.scala b/scalafix/output/src/main/scala/example/V0_4_0Rewrites.scala new file mode 100644 index 00000000..ada6c7c7 --- /dev/null +++ b/scalafix/output/src/main/scala/example/V0_4_0Rewrites.scala @@ -0,0 +1,13 @@ +package example + +// format: off +import feral.lambda.natchez.{AwsTags, KernelSource, TracedHandler} +// format: on + +object V0_4_0RewritesInput { + + val tags: AwsTags = ??? + val source: KernelSource = ??? + val handler: TracedHandler = ??? + +} From bb2970f7448640a4893dea59ad45cf5c978d8db7 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Mon, 30 Sep 2024 17:14:31 +0100 Subject: [PATCH 57/64] Split 0.3.0 and 0.4.0 rewrite rules into subprojects --- build.sbt | 33 +++++++++++++++++-- .../main/scala/example/V0_3_0Rewrites.scala | 0 .../main/scala/example/V0_3_0Rewrites.scala | 0 .../META-INF/services/scalafix.v1.Rule | 0 .../scala/feral/scalafix/V0_3_0Rewrites.scala | 0 .../test/scala/feral/scalafix/RuleSuite.scala | 0 .../main/scala/example/V0_4_0Rewrites.scala | 0 .../main/scala/example/V0_4_0Rewrites.scala | 0 .../META-INF/services/scalafix.v1.Rule | 1 + .../scala/feral/scalafix/V0_4_0Rewrites.scala | 0 .../test/scala/feral/scalafix/RuleSuite.scala | 24 ++++++++++++++ 11 files changed, 55 insertions(+), 3 deletions(-) rename scalafix/{ => 0_3_0}/input/src/main/scala/example/V0_3_0Rewrites.scala (100%) rename scalafix/{ => 0_3_0}/output/src/main/scala/example/V0_3_0Rewrites.scala (100%) rename scalafix/{ => 0_3_0}/rules/src/main/resources/META-INF/services/scalafix.v1.Rule (100%) rename scalafix/{ => 0_3_0}/rules/src/main/scala/feral/scalafix/V0_3_0Rewrites.scala (100%) rename scalafix/{ => 0_3_0}/tests/src/test/scala/feral/scalafix/RuleSuite.scala (100%) rename scalafix/{ => 0_4_0}/input/src/main/scala/example/V0_4_0Rewrites.scala (100%) rename scalafix/{ => 0_4_0}/output/src/main/scala/example/V0_4_0Rewrites.scala (100%) create mode 100644 scalafix/0_4_0/rules/src/main/resources/META-INF/services/scalafix.v1.Rule rename scalafix/{ => 0_4_0}/rules/src/main/scala/feral/scalafix/V0_4_0Rewrites.scala (100%) create mode 100644 scalafix/0_4_0/tests/src/test/scala/feral/scalafix/RuleSuite.scala diff --git a/build.sbt b/build.sbt index 5e9e16b7..cf1f6bb2 100644 --- a/build.sbt +++ b/build.sbt @@ -77,13 +77,16 @@ lazy val root = unidocs ) .configureRoot( - _.aggregate(sbtLambda).aggregate(scalafix.componentProjectReferences: _*) + _.aggregate(sbtLambda) + .aggregate(scalafix_0_3_0.componentProjectReferences: _*) + .aggregate(scalafix_0_4_0.componentProjectReferences: _*) ) lazy val rootSbtScalafix = project .in(file(".rootSbtScalafix")) .aggregate(sbtLambda) - .aggregate(scalafix.componentProjectReferences: _*) + .aggregate(scalafix_0_3_0.componentProjectReferences: _*) + .aggregate(scalafix_0_4_0.componentProjectReferences: _*) .enablePlugins(NoPublishPlugin) lazy val lambda = crossProject(JSPlatform, JVMPlatform) @@ -244,7 +247,8 @@ lazy val unidocs = project } ) -lazy val scalafix = tlScalafixProject +lazy val scalafix_0_3_0 = tlScalafixProject + .in(file("scalafix/0_3_0")) .rulesSettings( name := "feral-scalafix", startYear := Some(2023), @@ -266,6 +270,29 @@ lazy val scalafix = tlScalafixProject crossScalaVersions := Seq(Scala212) ) +lazy val scalafix_0_4_0 = tlScalafixProject + .in(file("scalafix/0_4_0")) + .rulesSettings( + name := "feral-scalafix-0_4_0", + startYear := Some(2023), + crossScalaVersions := Seq(Scala212) + ) + .inputSettings( + crossScalaVersions := Seq(Scala213), + libraryDependencies += "org.typelevel" %%% "feral-lambda" % "0.3.1", + headerSources / excludeFilter := AllPassFilter + ) + .inputConfigure(_.disablePlugins(ScalafixPlugin)) + .outputSettings( + crossScalaVersions := Seq(Scala213), + headerSources / excludeFilter := AllPassFilter + ) + .outputConfigure(_.dependsOn(lambdaHttp4s.jvm).disablePlugins(ScalafixPlugin)) + .testsSettings( + startYear := Some(2023), + crossScalaVersions := Seq(Scala212) + ) + lazy val googleCloudHttp4s = crossProject(JSPlatform, JVMPlatform) .in(file("google-cloud-http4s")) .settings( diff --git a/scalafix/input/src/main/scala/example/V0_3_0Rewrites.scala b/scalafix/0_3_0/input/src/main/scala/example/V0_3_0Rewrites.scala similarity index 100% rename from scalafix/input/src/main/scala/example/V0_3_0Rewrites.scala rename to scalafix/0_3_0/input/src/main/scala/example/V0_3_0Rewrites.scala diff --git a/scalafix/output/src/main/scala/example/V0_3_0Rewrites.scala b/scalafix/0_3_0/output/src/main/scala/example/V0_3_0Rewrites.scala similarity index 100% rename from scalafix/output/src/main/scala/example/V0_3_0Rewrites.scala rename to scalafix/0_3_0/output/src/main/scala/example/V0_3_0Rewrites.scala diff --git a/scalafix/rules/src/main/resources/META-INF/services/scalafix.v1.Rule b/scalafix/0_3_0/rules/src/main/resources/META-INF/services/scalafix.v1.Rule similarity index 100% rename from scalafix/rules/src/main/resources/META-INF/services/scalafix.v1.Rule rename to scalafix/0_3_0/rules/src/main/resources/META-INF/services/scalafix.v1.Rule diff --git a/scalafix/rules/src/main/scala/feral/scalafix/V0_3_0Rewrites.scala b/scalafix/0_3_0/rules/src/main/scala/feral/scalafix/V0_3_0Rewrites.scala similarity index 100% rename from scalafix/rules/src/main/scala/feral/scalafix/V0_3_0Rewrites.scala rename to scalafix/0_3_0/rules/src/main/scala/feral/scalafix/V0_3_0Rewrites.scala diff --git a/scalafix/tests/src/test/scala/feral/scalafix/RuleSuite.scala b/scalafix/0_3_0/tests/src/test/scala/feral/scalafix/RuleSuite.scala similarity index 100% rename from scalafix/tests/src/test/scala/feral/scalafix/RuleSuite.scala rename to scalafix/0_3_0/tests/src/test/scala/feral/scalafix/RuleSuite.scala diff --git a/scalafix/input/src/main/scala/example/V0_4_0Rewrites.scala b/scalafix/0_4_0/input/src/main/scala/example/V0_4_0Rewrites.scala similarity index 100% rename from scalafix/input/src/main/scala/example/V0_4_0Rewrites.scala rename to scalafix/0_4_0/input/src/main/scala/example/V0_4_0Rewrites.scala diff --git a/scalafix/output/src/main/scala/example/V0_4_0Rewrites.scala b/scalafix/0_4_0/output/src/main/scala/example/V0_4_0Rewrites.scala similarity index 100% rename from scalafix/output/src/main/scala/example/V0_4_0Rewrites.scala rename to scalafix/0_4_0/output/src/main/scala/example/V0_4_0Rewrites.scala diff --git a/scalafix/0_4_0/rules/src/main/resources/META-INF/services/scalafix.v1.Rule b/scalafix/0_4_0/rules/src/main/resources/META-INF/services/scalafix.v1.Rule new file mode 100644 index 00000000..5356dae0 --- /dev/null +++ b/scalafix/0_4_0/rules/src/main/resources/META-INF/services/scalafix.v1.Rule @@ -0,0 +1 @@ +feral.scalafix.V0_4_0Rewrites diff --git a/scalafix/rules/src/main/scala/feral/scalafix/V0_4_0Rewrites.scala b/scalafix/0_4_0/rules/src/main/scala/feral/scalafix/V0_4_0Rewrites.scala similarity index 100% rename from scalafix/rules/src/main/scala/feral/scalafix/V0_4_0Rewrites.scala rename to scalafix/0_4_0/rules/src/main/scala/feral/scalafix/V0_4_0Rewrites.scala diff --git a/scalafix/0_4_0/tests/src/test/scala/feral/scalafix/RuleSuite.scala b/scalafix/0_4_0/tests/src/test/scala/feral/scalafix/RuleSuite.scala new file mode 100644 index 00000000..a5ae13b2 --- /dev/null +++ b/scalafix/0_4_0/tests/src/test/scala/feral/scalafix/RuleSuite.scala @@ -0,0 +1,24 @@ +/* + * Copyright 2023 Typelevel + * + * Licensed 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 feral.scalafix + +import org.scalatest.funsuite.AnyFunSuiteLike +import scalafix.testkit._ + +class RuleSuite extends AbstractSemanticRuleSuite with AnyFunSuiteLike { + runAllTests() +} From a99a65607006794baa96f8956a32339a4de277a0 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Mon, 30 Sep 2024 17:22:58 +0100 Subject: [PATCH 58/64] Update workflows --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 75d37211..6387daba 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -118,11 +118,11 @@ jobs: - name: Make target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') - run: mkdir -p lambda-natchez/jvm/target lambda-natchez/js/target lambda-cloudformation-custom-resource/.js/target lambda-http4s/.jvm/target unidocs/target lambda-http4s/.js/target lambda/js/target scalafix/rules/target lambda/jvm/target lambda-otel4s/jvm/target sbt-lambda/target lambda-otel4s/js/target google-cloud-http4s/jvm/target lambda-cloudformation-custom-resource/.jvm/target google-cloud-http4s/js/target project/target + run: mkdir -p lambda-natchez/jvm/target lambda-natchez/js/target lambda-cloudformation-custom-resource/.js/target lambda-http4s/.jvm/target unidocs/target scalafix/0_3_0/rules/target lambda-http4s/.js/target lambda/js/target lambda/jvm/target lambda-otel4s/jvm/target sbt-lambda/target lambda-otel4s/js/target scalafix/0_4_0/rules/target google-cloud-http4s/jvm/target lambda-cloudformation-custom-resource/.jvm/target google-cloud-http4s/js/target project/target - name: Compress target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') - run: tar cf targets.tar lambda-natchez/jvm/target lambda-natchez/js/target lambda-cloudformation-custom-resource/.js/target lambda-http4s/.jvm/target unidocs/target lambda-http4s/.js/target lambda/js/target scalafix/rules/target lambda/jvm/target lambda-otel4s/jvm/target sbt-lambda/target lambda-otel4s/js/target google-cloud-http4s/jvm/target lambda-cloudformation-custom-resource/.jvm/target google-cloud-http4s/js/target project/target + run: tar cf targets.tar lambda-natchez/jvm/target lambda-natchez/js/target lambda-cloudformation-custom-resource/.js/target lambda-http4s/.jvm/target unidocs/target scalafix/0_3_0/rules/target lambda-http4s/.js/target lambda/js/target lambda/jvm/target lambda-otel4s/jvm/target sbt-lambda/target lambda-otel4s/js/target scalafix/0_4_0/rules/target google-cloud-http4s/jvm/target lambda-cloudformation-custom-resource/.jvm/target google-cloud-http4s/js/target project/target - name: Upload target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') @@ -297,5 +297,5 @@ jobs: - name: Submit Dependencies uses: scalacenter/sbt-dependency-submission@v2 with: - modules-ignore: rootjs_2.12 rootjs_3 rootjs_2.13 scalafix-output_2.13 scalafix-input_2.13 examples_sjs1_3 examples_sjs1_2.13 rootsbtscalafix_2.12 rootsbtscalafix_3 rootsbtscalafix_2.13 rootjvm_2.12 rootjvm_3 rootjvm_2.13 rootnative_2.12 rootnative_3 rootnative_2.13 examples_3 examples_2.13 scalafix-tests_2.12 + modules-ignore: scalafix_0_4_0-output_2.13 rootjs_2.12 rootjs_3 rootjs_2.13 examples_sjs1_3 examples_sjs1_2.13 scalafix_0_3_0-tests_2.12 scalafix_0_4_0-tests_2.12 rootsbtscalafix_2.12 rootsbtscalafix_3 rootsbtscalafix_2.13 rootjvm_2.12 rootjvm_3 rootjvm_2.13 rootnative_2.12 rootnative_3 rootnative_2.13 examples_3 examples_2.13 scalafix_0_4_0-input_2.13 scalafix_0_3_0-input_2.13 scalafix_0_3_0-output_2.13 configs-ignore: test scala-tool scala-doc-tool test-internal From 9b5d7d623ec865746f51fdb9687498906952a963 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Mon, 30 Sep 2024 17:26:47 +0100 Subject: [PATCH 59/64] Fix scalafix 0.4.0 rewrites test --- build.sbt | 4 ++-- .../src/main/scala/example/V0_4_0Rewrites.scala | 15 ++++++++++++--- .../src/main/scala/example/V0_4_0Rewrites.scala | 17 +++++++++++++---- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/build.sbt b/build.sbt index cf1f6bb2..b2aa45a1 100644 --- a/build.sbt +++ b/build.sbt @@ -46,7 +46,7 @@ ThisBuild / githubWorkflowBuildPreamble += ) val Scala212 = "2.12.19" -val Scala213 = "2.13.14" +val Scala213 = "2.13.15" val Scala3 = "3.3.3" ThisBuild / crossScalaVersions := Seq(Scala212, Scala3, Scala213) @@ -287,7 +287,7 @@ lazy val scalafix_0_4_0 = tlScalafixProject crossScalaVersions := Seq(Scala213), headerSources / excludeFilter := AllPassFilter ) - .outputConfigure(_.dependsOn(lambdaHttp4s.jvm).disablePlugins(ScalafixPlugin)) + .outputConfigure(_.dependsOn(lambdaNatchez.jvm).disablePlugins(ScalafixPlugin)) .testsSettings( startYear := Some(2023), crossScalaVersions := Seq(Scala212) diff --git a/scalafix/0_4_0/input/src/main/scala/example/V0_4_0Rewrites.scala b/scalafix/0_4_0/input/src/main/scala/example/V0_4_0Rewrites.scala index 31b27aae..d74cda51 100644 --- a/scalafix/0_4_0/input/src/main/scala/example/V0_4_0Rewrites.scala +++ b/scalafix/0_4_0/input/src/main/scala/example/V0_4_0Rewrites.scala @@ -3,15 +3,24 @@ package example // format: off +import cats.effect.IO import feral.lambda.AwsTags import feral.lambda.KernelSource import feral.lambda.TracedHandler +import feral.lambda.Invocation +import natchez.Trace +import natchez.EntryPoint // format: on object V0_4_0RewritesInput { - val tags: AwsTags = ??? - val source: KernelSource = ??? - val handler: TracedHandler = ??? + def useTags = AwsTags.arn("") + def source = KernelSource.emptyKernelSource[String] + def tracedHandler[Event, Result](entrypoint: EntryPoint[IO])( + handler: Trace[IO] => IO[Option[Result]] + )( + implicit env: Invocation[IO, Event], + KS: KernelSource[Event] + ): IO[Option[Result]] = TracedHandler(entrypoint)(handler) } diff --git a/scalafix/0_4_0/output/src/main/scala/example/V0_4_0Rewrites.scala b/scalafix/0_4_0/output/src/main/scala/example/V0_4_0Rewrites.scala index ada6c7c7..28398d74 100644 --- a/scalafix/0_4_0/output/src/main/scala/example/V0_4_0Rewrites.scala +++ b/scalafix/0_4_0/output/src/main/scala/example/V0_4_0Rewrites.scala @@ -1,13 +1,22 @@ package example // format: off -import feral.lambda.natchez.{AwsTags, KernelSource, TracedHandler} +import cats.effect.IO +import feral.lambda.Invocation +import natchez.Trace +import natchez.EntryPoint +import feral.lambda.natchez.{ AwsTags, KernelSource, TracedHandler } // format: on object V0_4_0RewritesInput { - val tags: AwsTags = ??? - val source: KernelSource = ??? - val handler: TracedHandler = ??? + def useTags = AwsTags.arn("") + def source = KernelSource.emptyKernelSource[String] + def tracedHandler[Event, Result](entrypoint: EntryPoint[IO])( + handler: Trace[IO] => IO[Option[Result]] + )( + implicit env: Invocation[IO, Event], + KS: KernelSource[Event] + ): IO[Option[Result]] = TracedHandler(entrypoint)(handler) } From ea168f59d8600ce47b54b3752df3c440b9a5443a Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Mon, 30 Sep 2024 22:05:03 +0100 Subject: [PATCH 60/64] Bump to scala 3.3.4 --- build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index b2aa45a1..39f303ef 100644 --- a/build.sbt +++ b/build.sbt @@ -47,7 +47,7 @@ ThisBuild / githubWorkflowBuildPreamble += val Scala212 = "2.12.19" val Scala213 = "2.13.15" -val Scala3 = "3.3.3" +val Scala3 = "3.3.4" ThisBuild / crossScalaVersions := Seq(Scala212, Scala3, Scala213) val catsEffectVersion = "3.5.4" From 9cca0f0249b092d5c1e6c095b1eab24f6d9b8fb7 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Mon, 30 Sep 2024 22:08:36 +0100 Subject: [PATCH 61/64] Remove 0.3.0 migrations --- .github/workflows/ci.yml | 6 ++-- build.sbt | 34 +++--------------- .../main/scala/example/V0_3_0Rewrites.scala | 36 ------------------- .../main/scala/example/V0_3_0Rewrites.scala | 28 --------------- .../META-INF/services/scalafix.v1.Rule | 1 - .../scala/feral/scalafix/V0_3_0Rewrites.scala | 36 ------------------- .../test/scala/feral/scalafix/RuleSuite.scala | 24 ------------- .../main/scala/example/V0_4_0Rewrites.scala | 0 .../main/scala/example/V0_4_0Rewrites.scala | 0 .../META-INF/services/scalafix.v1.Rule | 0 .../scala/feral/scalafix/V0_4_0Rewrites.scala | 0 .../test/scala/feral/scalafix/RuleSuite.scala | 0 12 files changed, 7 insertions(+), 158 deletions(-) delete mode 100644 scalafix/0_3_0/input/src/main/scala/example/V0_3_0Rewrites.scala delete mode 100644 scalafix/0_3_0/output/src/main/scala/example/V0_3_0Rewrites.scala delete mode 100644 scalafix/0_3_0/rules/src/main/resources/META-INF/services/scalafix.v1.Rule delete mode 100644 scalafix/0_3_0/rules/src/main/scala/feral/scalafix/V0_3_0Rewrites.scala delete mode 100644 scalafix/0_4_0/tests/src/test/scala/feral/scalafix/RuleSuite.scala rename scalafix/{0_4_0 => }/input/src/main/scala/example/V0_4_0Rewrites.scala (100%) rename scalafix/{0_4_0 => }/output/src/main/scala/example/V0_4_0Rewrites.scala (100%) rename scalafix/{0_4_0 => }/rules/src/main/resources/META-INF/services/scalafix.v1.Rule (100%) rename scalafix/{0_4_0 => }/rules/src/main/scala/feral/scalafix/V0_4_0Rewrites.scala (100%) rename scalafix/{0_3_0 => }/tests/src/test/scala/feral/scalafix/RuleSuite.scala (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6387daba..75d37211 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -118,11 +118,11 @@ jobs: - name: Make target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') - run: mkdir -p lambda-natchez/jvm/target lambda-natchez/js/target lambda-cloudformation-custom-resource/.js/target lambda-http4s/.jvm/target unidocs/target scalafix/0_3_0/rules/target lambda-http4s/.js/target lambda/js/target lambda/jvm/target lambda-otel4s/jvm/target sbt-lambda/target lambda-otel4s/js/target scalafix/0_4_0/rules/target google-cloud-http4s/jvm/target lambda-cloudformation-custom-resource/.jvm/target google-cloud-http4s/js/target project/target + run: mkdir -p lambda-natchez/jvm/target lambda-natchez/js/target lambda-cloudformation-custom-resource/.js/target lambda-http4s/.jvm/target unidocs/target lambda-http4s/.js/target lambda/js/target scalafix/rules/target lambda/jvm/target lambda-otel4s/jvm/target sbt-lambda/target lambda-otel4s/js/target google-cloud-http4s/jvm/target lambda-cloudformation-custom-resource/.jvm/target google-cloud-http4s/js/target project/target - name: Compress target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') - run: tar cf targets.tar lambda-natchez/jvm/target lambda-natchez/js/target lambda-cloudformation-custom-resource/.js/target lambda-http4s/.jvm/target unidocs/target scalafix/0_3_0/rules/target lambda-http4s/.js/target lambda/js/target lambda/jvm/target lambda-otel4s/jvm/target sbt-lambda/target lambda-otel4s/js/target scalafix/0_4_0/rules/target google-cloud-http4s/jvm/target lambda-cloudformation-custom-resource/.jvm/target google-cloud-http4s/js/target project/target + run: tar cf targets.tar lambda-natchez/jvm/target lambda-natchez/js/target lambda-cloudformation-custom-resource/.js/target lambda-http4s/.jvm/target unidocs/target lambda-http4s/.js/target lambda/js/target scalafix/rules/target lambda/jvm/target lambda-otel4s/jvm/target sbt-lambda/target lambda-otel4s/js/target google-cloud-http4s/jvm/target lambda-cloudformation-custom-resource/.jvm/target google-cloud-http4s/js/target project/target - name: Upload target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') @@ -297,5 +297,5 @@ jobs: - name: Submit Dependencies uses: scalacenter/sbt-dependency-submission@v2 with: - modules-ignore: scalafix_0_4_0-output_2.13 rootjs_2.12 rootjs_3 rootjs_2.13 examples_sjs1_3 examples_sjs1_2.13 scalafix_0_3_0-tests_2.12 scalafix_0_4_0-tests_2.12 rootsbtscalafix_2.12 rootsbtscalafix_3 rootsbtscalafix_2.13 rootjvm_2.12 rootjvm_3 rootjvm_2.13 rootnative_2.12 rootnative_3 rootnative_2.13 examples_3 examples_2.13 scalafix_0_4_0-input_2.13 scalafix_0_3_0-input_2.13 scalafix_0_3_0-output_2.13 + modules-ignore: rootjs_2.12 rootjs_3 rootjs_2.13 scalafix-output_2.13 scalafix-input_2.13 examples_sjs1_3 examples_sjs1_2.13 rootsbtscalafix_2.12 rootsbtscalafix_3 rootsbtscalafix_2.13 rootjvm_2.12 rootjvm_3 rootjvm_2.13 rootnative_2.12 rootnative_3 rootnative_2.13 examples_3 examples_2.13 scalafix-tests_2.12 configs-ignore: test scala-tool scala-doc-tool test-internal diff --git a/build.sbt b/build.sbt index 39f303ef..efeac68f 100644 --- a/build.sbt +++ b/build.sbt @@ -77,16 +77,13 @@ lazy val root = unidocs ) .configureRoot( - _.aggregate(sbtLambda) - .aggregate(scalafix_0_3_0.componentProjectReferences: _*) - .aggregate(scalafix_0_4_0.componentProjectReferences: _*) + _.aggregate(sbtLambda).aggregate(scalafix.componentProjectReferences: _*) ) lazy val rootSbtScalafix = project .in(file(".rootSbtScalafix")) .aggregate(sbtLambda) - .aggregate(scalafix_0_3_0.componentProjectReferences: _*) - .aggregate(scalafix_0_4_0.componentProjectReferences: _*) + .aggregate(scalafix.componentProjectReferences: _*) .enablePlugins(NoPublishPlugin) lazy val lambda = crossProject(JSPlatform, JVMPlatform) @@ -247,36 +244,13 @@ lazy val unidocs = project } ) -lazy val scalafix_0_3_0 = tlScalafixProject - .in(file("scalafix/0_3_0")) +lazy val scalafix = tlScalafixProject + .in(file("scalafix")) .rulesSettings( name := "feral-scalafix", startYear := Some(2023), crossScalaVersions := Seq(Scala212) ) - .inputSettings( - crossScalaVersions := Seq(Scala213), - libraryDependencies += "org.typelevel" %%% "feral-lambda-http4s" % "0.2.4", - headerSources / excludeFilter := AllPassFilter - ) - .inputConfigure(_.disablePlugins(ScalafixPlugin)) - .outputSettings( - crossScalaVersions := Seq(Scala213), - headerSources / excludeFilter := AllPassFilter - ) - .outputConfigure(_.dependsOn(lambdaHttp4s.jvm).disablePlugins(ScalafixPlugin)) - .testsSettings( - startYear := Some(2023), - crossScalaVersions := Seq(Scala212) - ) - -lazy val scalafix_0_4_0 = tlScalafixProject - .in(file("scalafix/0_4_0")) - .rulesSettings( - name := "feral-scalafix-0_4_0", - startYear := Some(2023), - crossScalaVersions := Seq(Scala212) - ) .inputSettings( crossScalaVersions := Seq(Scala213), libraryDependencies += "org.typelevel" %%% "feral-lambda" % "0.3.1", diff --git a/scalafix/0_3_0/input/src/main/scala/example/V0_3_0Rewrites.scala b/scalafix/0_3_0/input/src/main/scala/example/V0_3_0Rewrites.scala deleted file mode 100644 index 0b60baa2..00000000 --- a/scalafix/0_3_0/input/src/main/scala/example/V0_3_0Rewrites.scala +++ /dev/null @@ -1,36 +0,0 @@ -/* rule=V0_3_0Rewrites */ - -package example - -// format: off -import cats.effect.Concurrent -import feral.lambda.LambdaEnv -import feral.lambda.ApiGatewayProxyLambdaEnv -import feral.lambda.DynamoDbStreamLambdaEnv -import feral.lambda.S3BatchLambdaEnv -import feral.lambda.SnsLambdaEnv -import feral.lambda.SqsLambdaEnv -import feral.lambda.events.APIGatewayProxyRequestEvent -import feral.lambda.events.APIGatewayProxyResponseEvent -import feral.lambda.events.ApiGatewayProxyStructuredResultV2 -import feral.lambda.http4s.ApiGatewayProxyHandler -import org.http4s.HttpApp -// format: on - -class Foo[F[_], E] { - - def bar(implicit env: LambdaEnv[F, E]): Unit = ??? - -} - -object Handlers { - def handler1[F[_]: Concurrent]( - implicit env: ApiGatewayProxyLambdaEnv[F] - ): F[Option[ApiGatewayProxyStructuredResultV2]] = - ApiGatewayProxyHandler.httpApp(HttpApp.notFound) - def handler2[F[_]](implicit env: DynamoDbStreamLambdaEnv[F]): Unit = ??? - def handler3[F[_]](implicit env: S3BatchLambdaEnv[F]): Unit = ??? - def handler4[F[_]](implicit env: SnsLambdaEnv[F]): Unit = ??? - def handler5[F[_]](implicit env: SqsLambdaEnv[F]): Unit = ??? - def handler6(event: APIGatewayProxyRequestEvent): APIGatewayProxyResponseEvent = ??? -} diff --git a/scalafix/0_3_0/output/src/main/scala/example/V0_3_0Rewrites.scala b/scalafix/0_3_0/output/src/main/scala/example/V0_3_0Rewrites.scala deleted file mode 100644 index 99b10faa..00000000 --- a/scalafix/0_3_0/output/src/main/scala/example/V0_3_0Rewrites.scala +++ /dev/null @@ -1,28 +0,0 @@ -package example - -// format: off -import cats.effect.Concurrent -import feral.lambda.events.ApiGatewayProxyStructuredResultV2 -import org.http4s.HttpApp -import feral.lambda.{ ApiGatewayProxyInvocationV2, DynamoDbStreamInvocation, Invocation, S3BatchInvocation, SnsInvocation, SqsInvocation } -import feral.lambda.events.{ ApiGatewayProxyEvent, ApiGatewayProxyResult } -import feral.lambda.http4s.ApiGatewayProxyHandlerV2 -// format: on - -class Foo[F[_], E] { - - def bar(implicit env: Invocation[F, E]): Unit = ??? - -} - -object Handlers { - def handler1[F[_]: Concurrent]( - implicit env: ApiGatewayProxyInvocationV2[F] - ): F[Option[ApiGatewayProxyStructuredResultV2]] = - ApiGatewayProxyHandlerV2.apply(HttpApp.notFound) - def handler2[F[_]](implicit env: DynamoDbStreamInvocation[F]): Unit = ??? - def handler3[F[_]](implicit env: S3BatchInvocation[F]): Unit = ??? - def handler4[F[_]](implicit env: SnsInvocation[F]): Unit = ??? - def handler5[F[_]](implicit env: SqsInvocation[F]): Unit = ??? - def handler6(event: ApiGatewayProxyEvent): ApiGatewayProxyResult = ??? -} diff --git a/scalafix/0_3_0/rules/src/main/resources/META-INF/services/scalafix.v1.Rule b/scalafix/0_3_0/rules/src/main/resources/META-INF/services/scalafix.v1.Rule deleted file mode 100644 index 505e297f..00000000 --- a/scalafix/0_3_0/rules/src/main/resources/META-INF/services/scalafix.v1.Rule +++ /dev/null @@ -1 +0,0 @@ -feral.scalafix.V0_3_0Rewrites diff --git a/scalafix/0_3_0/rules/src/main/scala/feral/scalafix/V0_3_0Rewrites.scala b/scalafix/0_3_0/rules/src/main/scala/feral/scalafix/V0_3_0Rewrites.scala deleted file mode 100644 index 9d320d1f..00000000 --- a/scalafix/0_3_0/rules/src/main/scala/feral/scalafix/V0_3_0Rewrites.scala +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2023 Typelevel - * - * Licensed 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 feral.scalafix - -import scalafix.v1._ - -class V0_3_0Rewrites extends SemanticRule("V0_3_0Rewrites") { - override def fix(implicit doc: SemanticDocument): Patch = - Patch.replaceSymbols( - "feral.lambda.LambdaEnv" -> "feral.lambda.Invocation", - "feral.lambda.ApiGatewayProxyLambdaEnv" -> "feral.lambda.ApiGatewayProxyInvocationV2", - "feral.lambda.DynamoDbStreamLambdaEnv" -> "feral.lambda.DynamoDbStreamInvocation", - "feral.lambda.S3BatchLambdaEnv" -> "feral.lambda.S3BatchInvocation", - "feral.lambda.SnsLambdaEnv" -> "feral.lambda.SnsInvocation", - "feral.lambda.SqsLambdaEnv" -> "feral.lambda.SqsInvocation", - "feral.lambda.events.APIGatewayProxyRequestEvent" -> "feral.lambda.events.ApiGatewayProxyEvent", - "feral.lambda.events.APIGatewayProxyResponseEvent" -> "feral.lambda.events.ApiGatewayProxyResult", - "feral.lambda.http4s.ApiGatewayProxyHandler.httpApp" -> "feral.lambda.http4s.ApiGatewayProxyHandlerV2.apply", - "feral.lambda.http4s.ApiGatewayProxyHandler.apply" -> "feral.lambda.http4s.ApiGatewayProxyHandlerV2.httpRoutes", - "feral.lambda.http4s.ApiGatewayProxyHandler.httpRoutes" -> "feral.lambda.http4s.ApiGatewayProxyHandlerV2.httpRoutes" - ) + Patch.removeGlobalImport(Symbol("feral/lambda/http4s/ApiGatewayProxyHandler.")) -} diff --git a/scalafix/0_4_0/tests/src/test/scala/feral/scalafix/RuleSuite.scala b/scalafix/0_4_0/tests/src/test/scala/feral/scalafix/RuleSuite.scala deleted file mode 100644 index a5ae13b2..00000000 --- a/scalafix/0_4_0/tests/src/test/scala/feral/scalafix/RuleSuite.scala +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2023 Typelevel - * - * Licensed 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 feral.scalafix - -import org.scalatest.funsuite.AnyFunSuiteLike -import scalafix.testkit._ - -class RuleSuite extends AbstractSemanticRuleSuite with AnyFunSuiteLike { - runAllTests() -} diff --git a/scalafix/0_4_0/input/src/main/scala/example/V0_4_0Rewrites.scala b/scalafix/input/src/main/scala/example/V0_4_0Rewrites.scala similarity index 100% rename from scalafix/0_4_0/input/src/main/scala/example/V0_4_0Rewrites.scala rename to scalafix/input/src/main/scala/example/V0_4_0Rewrites.scala diff --git a/scalafix/0_4_0/output/src/main/scala/example/V0_4_0Rewrites.scala b/scalafix/output/src/main/scala/example/V0_4_0Rewrites.scala similarity index 100% rename from scalafix/0_4_0/output/src/main/scala/example/V0_4_0Rewrites.scala rename to scalafix/output/src/main/scala/example/V0_4_0Rewrites.scala diff --git a/scalafix/0_4_0/rules/src/main/resources/META-INF/services/scalafix.v1.Rule b/scalafix/rules/src/main/resources/META-INF/services/scalafix.v1.Rule similarity index 100% rename from scalafix/0_4_0/rules/src/main/resources/META-INF/services/scalafix.v1.Rule rename to scalafix/rules/src/main/resources/META-INF/services/scalafix.v1.Rule diff --git a/scalafix/0_4_0/rules/src/main/scala/feral/scalafix/V0_4_0Rewrites.scala b/scalafix/rules/src/main/scala/feral/scalafix/V0_4_0Rewrites.scala similarity index 100% rename from scalafix/0_4_0/rules/src/main/scala/feral/scalafix/V0_4_0Rewrites.scala rename to scalafix/rules/src/main/scala/feral/scalafix/V0_4_0Rewrites.scala diff --git a/scalafix/0_3_0/tests/src/test/scala/feral/scalafix/RuleSuite.scala b/scalafix/tests/src/test/scala/feral/scalafix/RuleSuite.scala similarity index 100% rename from scalafix/0_3_0/tests/src/test/scala/feral/scalafix/RuleSuite.scala rename to scalafix/tests/src/test/scala/feral/scalafix/RuleSuite.scala From c46d6d3deff7990bdbe4c4cd655a4788f195001b Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Mon, 30 Sep 2024 22:12:06 +0100 Subject: [PATCH 62/64] Bump sbt-lambda scalaVersion --- .../src/sbt-test/lambda-js-plugin/iolambda-simple/build.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sbt-lambda/src/sbt-test/lambda-js-plugin/iolambda-simple/build.sbt b/sbt-lambda/src/sbt-test/lambda-js-plugin/iolambda-simple/build.sbt index ddf1e789..64b04c03 100644 --- a/sbt-lambda/src/sbt-test/lambda-js-plugin/iolambda-simple/build.sbt +++ b/sbt-lambda/src/sbt-test/lambda-js-plugin/iolambda-simple/build.sbt @@ -1,2 +1,2 @@ -scalaVersion := "2.13.14" +scalaVersion := "2.13.15" enablePlugins(LambdaJSPlugin) From 15d8febf00d38a82152ff1a59e81cc975815fd61 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Tue, 1 Oct 2024 09:44:44 +0100 Subject: [PATCH 63/64] Remove unused examples imports --- .../jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala b/examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala index 9059893b..ab273965 100644 --- a/examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala +++ b/examples/jvm/src/main/scala/feral/examples/SqsOtelJavaExample.scala @@ -24,8 +24,6 @@ import feral.lambda.IOLambda import feral.lambda.Invocation import feral.lambda.events.SqsEvent import feral.lambda.events.SqsRecord -import feral.lambda.otel4s.SqsRecordAttributes -import feral.lambda.otel4s.TracedHandler import feral.lambda.otel4s._ import org.http4s.client.Client import org.http4s.ember.client.EmberClientBuilder From fb3e02983bc595ef29f18a5fa54ab310301d8007 Mon Sep 17 00:00:00 2001 From: alexcardell <29524087+alexcardell@users.noreply.github.com> Date: Fri, 4 Oct 2024 10:12:08 +0100 Subject: [PATCH 64/64] Rename EventSpanAttributes -> EventAttributeSources --- ...urces.scala => EventAttributeSource.scala} | 38 +++++++++++++------ .../lambda/otel4s/EventSpanAttributes.scala | 38 ------------------- .../feral/lambda/otel4s/TracedHandler.scala | 4 +- .../scala/feral/lambda/otel4s/package.scala | 19 ---------- .../lambda/otel4s/TracedHandlerSuite.scala | 4 +- 5 files changed, 30 insertions(+), 73 deletions(-) rename lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/{EventAttributeSources.scala => EventAttributeSource.scala} (67%) delete mode 100644 lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventSpanAttributes.scala delete mode 100644 lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/package.scala diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributeSources.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributeSource.scala similarity index 67% rename from lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributeSources.scala rename to lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributeSource.scala index 6a15ccea..eafacb1a 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributeSources.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventAttributeSource.scala @@ -24,9 +24,24 @@ import feral.lambda.events.SqsEvent import org.typelevel.otel4s.Attributes import org.typelevel.otel4s.trace.SpanKind -private[otel4s] trait EventAttributeSources { - implicit def sqsEvent: EventSpanAttributes[SqsEvent] = - new EventSpanAttributes[SqsEvent] { +trait EventAttributeSource[E] { + def contextCarrier(e: E): Map[String, String] + def spanKind: SpanKind + def attributes(e: E): Attributes +} + +object EventAttributeSource { + def empty[E](sk: SpanKind): EventAttributeSource[E] = + new EventAttributeSource[E] { + def contextCarrier(e: E): Map[String, String] = + Map.empty + def spanKind: SpanKind = sk + def attributes(e: E): Attributes = + Attributes.empty + } + + implicit def sqsEvent: EventAttributeSource[SqsEvent] = + new EventAttributeSource[SqsEvent] { def contextCarrier(e: SqsEvent): Map[String, String] = Map.empty @@ -36,8 +51,8 @@ private[otel4s] trait EventAttributeSources { SqsEventAttributes() } - implicit def dynamoDbStreamEvent: EventSpanAttributes[DynamoDbStreamEvent] = - new EventSpanAttributes[DynamoDbStreamEvent] { + implicit def dynamoDbStreamEvent: EventAttributeSource[DynamoDbStreamEvent] = + new EventAttributeSource[DynamoDbStreamEvent] { def contextCarrier(e: DynamoDbStreamEvent): Map[String, String] = Map.empty @@ -47,8 +62,8 @@ private[otel4s] trait EventAttributeSources { DynamoDbStreamEventAttributes() } - implicit def apiGatewayProxyEvent: EventSpanAttributes[ApiGatewayProxyEvent] = - new EventSpanAttributes[ApiGatewayProxyEvent] { + implicit def apiGatewayProxyEvent: EventAttributeSource[ApiGatewayProxyEvent] = + new EventAttributeSource[ApiGatewayProxyEvent] { def contextCarrier(e: ApiGatewayProxyEvent): Map[String, String] = e.headers.getOrElse(Map.empty).map { case (k, v) => (k.toString, v) } @@ -58,8 +73,8 @@ private[otel4s] trait EventAttributeSources { ApiGatewayProxyEventAttributes() } - implicit def apiGatewayProxyEventV2: EventSpanAttributes[ApiGatewayProxyEventV2] = - new EventSpanAttributes[ApiGatewayProxyEventV2] { + implicit def apiGatewayProxyEventV2: EventAttributeSource[ApiGatewayProxyEventV2] = + new EventAttributeSource[ApiGatewayProxyEventV2] { def contextCarrier(e: ApiGatewayProxyEventV2): Map[String, String] = e.headers.map { case (k, v) => (k.toString, v) } @@ -69,8 +84,8 @@ private[otel4s] trait EventAttributeSources { ApiGatewayProxyEventAttributes() } - implicit def s3BatchEvent: EventSpanAttributes[S3BatchEvent] = - new EventSpanAttributes[S3BatchEvent] { + implicit def s3BatchEvent: EventAttributeSource[S3BatchEvent] = + new EventAttributeSource[S3BatchEvent] { def contextCarrier(e: S3BatchEvent): Map[String, String] = Map.empty @@ -79,5 +94,4 @@ private[otel4s] trait EventAttributeSources { def attributes(e: S3BatchEvent): Attributes = S3BatchEventAttributes(e) } - } diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventSpanAttributes.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventSpanAttributes.scala deleted file mode 100644 index bad79d42..00000000 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/EventSpanAttributes.scala +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2021 Typelevel - * - * Licensed 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 feral.lambda.otel4s - -import org.typelevel.otel4s.Attributes -import org.typelevel.otel4s.trace.SpanKind - -// TODO better name -private[otel4s] trait EventSpanAttributes[E] { - def contextCarrier(e: E): Map[String, String] - def spanKind: SpanKind - def attributes(e: E): Attributes -} - -private[otel4s] object EventSpanAttributes { - def empty[E](sk: SpanKind): EventSpanAttributes[E] = - new EventSpanAttributes[E] { - def contextCarrier(e: E): Map[String, String] = - Map.empty - def spanKind: SpanKind = sk - def attributes(e: E): Attributes = - Attributes.empty - } -} diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala index d2cc9724..4f92eb5d 100644 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala +++ b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/TracedHandler.scala @@ -29,7 +29,7 @@ object TracedHandler { handler: F[Option[Result]] )( implicit inv: Invocation[F, Event], - attr: EventSpanAttributes[Event] + attr: EventAttributeSource[Event] ): F[Option[Result]] = for { event <- inv.event @@ -42,7 +42,7 @@ object TracedHandler { } yield res private def buildSpan[F[_]: Tracer, Event](event: Event, context: Context[F])( - implicit attr: EventSpanAttributes[Event] + implicit attr: EventAttributeSource[Event] ): SpanOps[F] = Tracer[F] .spanBuilder(context.functionName) diff --git a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/package.scala b/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/package.scala deleted file mode 100644 index 9d016a73..00000000 --- a/lambda-otel4s/shared/src/main/scala/feral/lambda/otel4s/package.scala +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2021 Typelevel - * - * Licensed 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 feral.lambda - -package object otel4s extends EventAttributeSources {} diff --git a/lambda-otel4s/shared/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala b/lambda-otel4s/shared/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala index ac136c18..55c34dd4 100644 --- a/lambda-otel4s/shared/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala +++ b/lambda-otel4s/shared/src/test/scala/feral/lambda/otel4s/TracedHandlerSuite.scala @@ -123,8 +123,8 @@ object SharedTracedHandlerSuite { implicit val encoder: Encoder[TestEvent] = Encoder.forProduct2("traceId", "payload")(ev => (ev.traceId, ev.payload)) - implicit val attr: EventSpanAttributes[TestEvent] = - new EventSpanAttributes[TestEvent] { + implicit val attr: EventAttributeSource[TestEvent] = + new EventAttributeSource[TestEvent] { override def contextCarrier(e: TestEvent): Map[String, String] = Map("trace_id" -> e.traceId)