-
Notifications
You must be signed in to change notification settings - Fork 78
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #643 from tpolecat/otel
Add OpenTelemetry support from #368
- Loading branch information
Showing
9 changed files
with
350 additions
and
47 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
# OpenTelemetry | ||
|
||
The `natchez-opentelemetry` module provides a backend that uses [OpenTelemetry](https://opentelemetry.io) to report spans. | ||
|
||
To use it, add the following dependency. | ||
|
||
@@dependency[sbt,Maven,Gradle] { | ||
group="$org$" | ||
artifact="natchez-opentelemetry-2.13" | ||
version="$version$" | ||
} | ||
|
||
Then add any exporter, for example: | ||
|
||
@@dependency[sbt,Maven,Gradle] { | ||
group="io.opentelemetry" | ||
artifact="opentelemetry-exporter-otlp" | ||
version="1.12.0" | ||
} | ||
|
||
## Configuring an OpenTelemetry entrypoint | ||
|
||
There are two methods you'll need to construct an `OpenTelemetry` `EndPoint`. | ||
|
||
`OpenTelemetry.lift` is used to turn an `F[_]` that constructs a `SpanExporter`, `SpanProcessor` or `SdkTraceProvider` into a `Resource` that will shut it down cleanly. | ||
This takes a `String` of what you've constructed, so we can give a nice error if it fails to shut down cleanly. | ||
|
||
The `OpenTelemetry.entryPoint` method takes a boolean called `globallyRegister` which tells it whether to register this `OpenTelemetry` globally. This may be helpful if you have other Java dependencies that use the global tracer. It defaults to false. | ||
|
||
It also takes an `OpenTelemetrySdkBuilder => Resource[F, OpenTelemetrySdkBuilder]` so that you can configure the Sdk. | ||
|
||
Here's an example of configuring one with the `otlp` exporter with batch span processing: | ||
|
||
```scala mdoc:passthrough | ||
import natchez.EntryPoint | ||
import natchez.opentelemetry.OpenTelemetry | ||
import cats.effect._ | ||
import io.opentelemetry.api.common.Attributes | ||
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes | ||
import io.opentelemetry.sdk.resources.{Resource => OtelResource} | ||
import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator | ||
import io.opentelemetry.context.propagation.ContextPropagators | ||
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter | ||
import io.opentelemetry.sdk.trace.SdkTracerProvider | ||
import io.opentelemetry.sdk.trace.`export`.BatchSpanProcessor | ||
|
||
def entryPoint[F[_]: Async]: Resource[F, EntryPoint[F]] = | ||
for { | ||
exporter <- OpenTelemetry.lift( | ||
"OtlpGrpcSpanExporter", | ||
Sync[F].delay { | ||
OtlpGrpcSpanExporter.builder() | ||
.setEndpoint("http://localhost:4317") | ||
.build() | ||
} | ||
) | ||
processor <- OpenTelemetry.lift( | ||
"BatchSpanProcessor", | ||
Sync[F].delay { | ||
BatchSpanProcessor.builder(exporter).build() | ||
} | ||
) | ||
tracer <- OpenTelemetry.lift( | ||
"Tracer", | ||
Sync[F].delay { | ||
SdkTracerProvider.builder() | ||
.setResource( | ||
OtelResource.create( | ||
Attributes.of(ResourceAttributes.SERVICE_NAME, "OpenTelemetryExample") | ||
) | ||
) | ||
.addSpanProcessor(processor) | ||
.build() | ||
} | ||
) | ||
ep <- OpenTelemetry.entryPoint(globallyRegister = true) { builder => | ||
Resource.eval(Sync[F].delay { | ||
builder | ||
.setTracerProvider(tracer) | ||
.setPropagators( | ||
ContextPropagators.create(W3CTraceContextPropagator.getInstance()) | ||
) | ||
} | ||
)} | ||
} yield ep | ||
``` |
82 changes: 82 additions & 0 deletions
82
modules/examples/src/main/scala/OpenTelemetryExample.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
// Copyright (c) 2019-2020 by Rob Norris and Contributors | ||
// This software is licensed under the MIT License (MIT). | ||
// For more information see LICENSE or https://opensource.org/licenses/MIT | ||
|
||
import cats.data.Kleisli | ||
import cats.effect._ | ||
import cats.implicits._ | ||
import io.opentelemetry.api.common.Attributes | ||
import io.opentelemetry.sdk.resources.{Resource => OtelResource} | ||
import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator | ||
import io.opentelemetry.context.propagation.ContextPropagators | ||
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter | ||
import io.opentelemetry.sdk.trace.SdkTracerProvider | ||
import io.opentelemetry.sdk.trace.`export`.BatchSpanProcessor | ||
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes | ||
import natchez.{EntryPoint, Span, Trace} | ||
import natchez.opentelemetry.OpenTelemetry | ||
|
||
import scala.concurrent.duration.DurationInt | ||
|
||
// change this into an object if you'd like to run it | ||
class OpenTelemetryExample extends IOApp { | ||
def entryPoint[F[_]: Async]: Resource[F, EntryPoint[F]] = | ||
for { | ||
exporter <- OpenTelemetry.lift( | ||
"OtlpGrpcSpanExporter", | ||
Sync[F].delay { | ||
OtlpGrpcSpanExporter.builder() | ||
.setEndpoint("http://localhost:4317") | ||
.build() | ||
} | ||
) | ||
processor <- OpenTelemetry.lift( | ||
"BatchSpanProcessor", | ||
Sync[F].delay { | ||
BatchSpanProcessor.builder(exporter).build() | ||
} | ||
) | ||
tracer <- OpenTelemetry.lift( | ||
"Tracer", | ||
Sync[F].delay { | ||
SdkTracerProvider.builder() | ||
.setResource( | ||
OtelResource.create( | ||
Attributes.of(ResourceAttributes.SERVICE_NAME, "OpenTelemetryExample") | ||
) | ||
) | ||
.addSpanProcessor(processor) | ||
.build() | ||
} | ||
) | ||
ep <- OpenTelemetry.entryPoint(globallyRegister = true) { builder => | ||
Resource.eval(Sync[F].delay { | ||
builder | ||
.setTracerProvider(tracer) | ||
.setPropagators( | ||
ContextPropagators.create(W3CTraceContextPropagator.getInstance()) | ||
) | ||
} | ||
)} | ||
} yield ep | ||
|
||
override def run(args: List[String]): IO[ExitCode] = | ||
entryPoint[IO].use { ep => | ||
ep.root("root span").use { span => | ||
span.put("service.name" -> "natchez opentelemetry example") *> | ||
program[Kleisli[IO, Span[IO], *]].apply(span).as(ExitCode.Success) | ||
} | ||
} | ||
|
||
def program[F[_]: Async: Trace]: F[Unit] = | ||
Trace[F].traceId.flatTap(tid => Sync[F].delay { println(s"did some work with traceid of $tid") }) *> | ||
Trace[F].span("outer span") { | ||
Trace[F].put("foo" -> "bar") *> | ||
(Trace[F].span("first thing") { | ||
Temporal[F].sleep(2.seconds) | ||
}, | ||
Trace[F].span("second thing") { | ||
Temporal[F].sleep(2.seconds) | ||
}).tupled | ||
}.void | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
26 changes: 26 additions & 0 deletions
26
modules/opentelemetry/src/main/scala/natchez/opentelemetry/OpenTelemetryEntryPoint.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// Copyright (c) 2019-2020 by Rob Norris and Contributors | ||
// This software is licensed under the MIT License (MIT). | ||
// For more information see LICENSE or https://opensource.org/licenses/MIT | ||
|
||
package natchez | ||
package opentelemetry | ||
|
||
import cats.effect.{Resource, Sync} | ||
import cats.syntax.all._ | ||
import io.opentelemetry.api.{OpenTelemetry => OTel} | ||
import io.opentelemetry.api.trace.Tracer | ||
|
||
import java.net.URI | ||
|
||
final case class OpenTelemetryEntryPoint[F[_] : Sync](sdk: OTel, tracer: Tracer, prefix: Option[URI]) extends EntryPoint[F] { | ||
override def continue(name: String, kernel: Kernel): Resource[F, Span[F]] = | ||
Resource.makeCase(OpenTelemetrySpan.fromKernel(sdk, tracer, prefix, name, kernel))(OpenTelemetrySpan.finish).widen | ||
|
||
override def root(name: String): Resource[F, Span[F]] = | ||
Resource.makeCase(OpenTelemetrySpan.root(sdk, tracer, prefix, name))(OpenTelemetrySpan.finish).widen | ||
|
||
override def continueOrElseRoot(name: String, kernel: Kernel): Resource[F, Span[F]] = | ||
Resource | ||
.makeCase(OpenTelemetrySpan.fromKernelOrElseRoot(sdk, tracer, prefix, name, kernel))(OpenTelemetrySpan.finish) | ||
.widen | ||
} |
Oops, something went wrong.