Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,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 plugin/target runtime/target/jvm-2.12 codegen/target/jvm-3 codegen/target/jvm-2.13 runtime/target/jvm-3 codegen/target/jvm-2.12 runtime/target/jvm-2.13 project/target
run: mkdir -p plugin/target otel4s-trace/target/jvm-3 runtime/target/jvm-2.12 codegen/target/jvm-3 codegen/target/jvm-2.13 runtime/target/jvm-3 codegen/target/jvm-2.12 runtime/target/jvm-2.13 otel4s-trace/target/jvm-2.13 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 plugin/target runtime/target/jvm-2.12 codegen/target/jvm-3 codegen/target/jvm-2.13 runtime/target/jvm-3 codegen/target/jvm-2.12 runtime/target/jvm-2.13 project/target
run: tar cf targets.tar plugin/target otel4s-trace/target/jvm-3 runtime/target/jvm-2.12 codegen/target/jvm-3 codegen/target/jvm-2.13 runtime/target/jvm-3 codegen/target/jvm-2.12 runtime/target/jvm-2.13 otel4s-trace/target/jvm-2.13 project/target

- name: Upload target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
Expand Down Expand Up @@ -224,7 +224,7 @@ jobs:
- name: Submit Dependencies
uses: scalacenter/sbt-dependency-submission@v2
with:
modules-ignore: root_3 e2e_2.12 e2e_3 e2e_2.13
modules-ignore: root_3 e2e_2.12 e2eotel4s_3 e2eotel4s_2.13 e2e_3 e2e_2.13
configs-ignore: test scala-tool scala-doc-tool test-internal

validate-steward:
Expand Down
45 changes: 45 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,51 @@ The full set of options available are:
PB.protocOptions in Compile := Seq("-xyz")
```

## OpenTelemetry tracing with otel4s

The `fs2-grpc-otel4s-trace` module provides client and service aspects for tracing with [otel4s](https://typelevel.org/otel4s/).
The default configuration follows the OpenTelemetry gRPC semantic conventions for span kind, span name,
`rpc.system.name`, `rpc.method`, and `rpc.response.status_code`.

```scala
import cats.effect.IO
import fs2.grpc.otel4s.trace.{TraceClientAspect, TraceServiceAspect}
import io.grpc.Metadata
import org.typelevel.otel4s.trace.TracerProvider

def clientAspect(implicit tracerProvider: TracerProvider[IO]) =
TraceClientAspect.create[IO]

def serviceAspect(implicit tracerProvider: TracerProvider[IO]) =
TraceServiceAspect.create[IO]
```

Use `withServerAddress` when the logical server address is known. The aspect cannot infer it from the generated call
context:

```scala
val clientConfig =
TraceClientAspect.Config.default
.withServerAddress("grpc.example.com", Some(443))

val serviceConfig =
TraceServiceAspect.Config.default
.withServerAddress("grpc.example.com", Some(443))
```

Other defaults can be customized through `Config`, for example span names, metadata propagation, attributes, and
finalization.

```scala
val clientConfig =
TraceClientAspect.Config.default
.withTracerName("my-client")
.withSpanName((_, ctx) => ctx.methodDescriptor.getFullMethodName)
```

See the [OpenTelemetry gRPC semantic conventions](https://opentelemetry.io/docs/specs/semconv/rpc/grpc/) for the
attribute definitions.

### Tool Sponsorship

<img width="185px" height="44px" align="right" src="https://www.yourkit.com/images/yklogo.png"/>Development of fs2-grpc is generously supported in part by [YourKit](https://www.yourkit.com) through the use of their excellent Java profiler.
Expand Down
61 changes: 59 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ inThisBuild(
)

lazy val projects =
runtime.projectRefs ++ codegen.projectRefs ++ e2e.projectRefs ++ List(plugin.project, protocGen.agg.project)
runtime.projectRefs ++ otel4sTrace.projectRefs ++ codegen.projectRefs ++ e2e.projectRefs ++ e2eOtel4s.projectRefs ++
List(plugin.project, protocGen.agg.project)

lazy val root = (project in file("."))
.enablePlugins(BuildInfoPlugin, NoPublishPlugin)
Expand Down Expand Up @@ -112,6 +113,7 @@ lazy val plugin = project

lazy val runtime = (projectMatrix in file("runtime"))
.defaultAxes(axesDefault: _*)
.enablePlugins(BuildInfoPlugin)
.settings(
name := "fs2-grpc-runtime",
tlVersionIntroduced := Map("2.12" -> "2.5.3", "2.13" -> "2.5.3", "3" -> "2.5.3"),
Expand All @@ -124,10 +126,32 @@ lazy val runtime = (projectMatrix in file("runtime"))
scalacOptions ++= {
if (tlIsScala3.value) { Seq("-language:implicitConversions", "-Ykind-projector", "-source:3.0-migration") }
else Seq.empty
}
},
buildInfoPackage := "fs2.grpc",
buildInfoKeys := Seq[BuildInfoKey](version),
buildInfoOptions += BuildInfoOption.PackagePrivate
)
.jvmPlatform(scalaVersions = Seq(Scala212, Scala213, Scala3))

lazy val otel4sTrace = (projectMatrix in file("otel4s-trace"))
.dependsOn(runtime)
.defaultAxes(axesDefault: _*)
.settings(
name := "fs2-grpc-otel4s-trace",
tlVersionIntroduced := Map("2.13" -> "3.1.0", "3" -> "3.1.0"),
libraryDependencies ++= List(otel4sCoreTrace, otel4sSemconv) ++ List(grpcNetty, ceTestkit, ceMunit).map(_ % Test),
Test / parallelExecution := false,
scalacOptions := {
if (tlIsScala3.value) { scalacOptions.value.filterNot(_ == "-Ykind-projector:underscores") }
else scalacOptions.value
},
scalacOptions ++= {
if (tlIsScala3.value) { Seq("-language:implicitConversions", "-Ykind-projector", "-source:3.0-migration") }
else Seq.empty
}
)
.jvmPlatform(scalaVersions = Seq(Scala213, Scala3))

lazy val codeGenJVM212 = codegen.jvm(Scala212)
lazy val protocGen = protocGenProject("protoc-gen-fs2-grpc", codeGenJVM212)
.settings(
Expand Down Expand Up @@ -194,3 +218,36 @@ lazy val e2e = (projectMatrix in file("e2e"))
}
)
.jvmPlatform(scalaVersions = Seq(Scala212, Scala213, Scala3))

lazy val e2eOtel4s = (projectMatrix in file("e2e-otel4s"))
.dependsOn(runtime, otel4sTrace)
.defaultAxes(axesDefault: _*)
.enablePlugins(LocalCodeGenPlugin, NoPublishPlugin)
.settings(
codeGenClasspath := (codeGenJVM212 / Compile / fullClasspath).value,
libraryDependencies := Nil,
libraryDependencies ++= List(
scalaPbGrpcRuntime,
scalaPbRuntime,
scalaPbRuntime % "protobuf",
ceMunit % Test,
otel4sOtelJavaTestkit % Test,
otel4sSemconvExperimental % Test,
"io.grpc" % "grpc-inprocess" % versions.grpc % Test
),
Compile / PB.targets := Seq(
scalapb.gen() -> (Compile / sourceManaged).value / "scalapb",
genModule(codegenFullName + "$") -> (Compile / sourceManaged).value / "fs2-grpc"
),
githubWorkflowArtifactUpload := false,
scalacOptions := {
if (tlIsScala3.value) {
scalacOptions.value.filterNot(o => o == "-Ykind-projector:underscores" || o == "-Wvalue-discard")
} else scalacOptions.value
},
scalacOptions ++= {
if (tlIsScala3.value) { Seq("-language:implicitConversions", "-Ykind-projector", "-source:3.0-migration") }
else Seq.empty
}
)
.jvmPlatform(scalaVersions = Seq(Scala213, Scala3))
19 changes: 19 additions & 0 deletions e2e-otel4s/src/main/protobuf/test_service.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
syntax = "proto3";

package hello.world;

// TestService: Example gRPC service used in e2e tests
// It demonstrates all four RPC shapes.
service TestService {
// Unary RPC: no streaming in either direction
rpc noStreaming (TestRequest) returns (TestResponse);
// Client streaming RPC: client streams, server returns a single response
rpc clientStreaming (stream TestRequest) returns (TestResponse);
// Server streaming RPC: client sends one request, server streams responses
rpc serverStreaming (TestRequest) returns (stream TestResponse);
// Bidirectional streaming RPC: both client and server stream
rpc bothStreaming (stream TestRequest) returns (stream TestResponse);
}

message TestRequest {}
message TestResponse {}
Loading
Loading