Skip to content

Commit

Permalink
Add opencensus client (#155)
Browse files Browse the repository at this point in the history
  • Loading branch information
frekw authored May 25, 2020
1 parent bb5ec7a commit 4b3b2df
Show file tree
Hide file tree
Showing 7 changed files with 340 additions and 0 deletions.
5 changes: 5 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ lazy val opentelemetry =
.settings(stdSettings("zio-opentelemetry"))
.settings(libraryDependencies := Dependencies.opentelemetry)

lazy val opencensus = project
.in(file("opencensus"))
.settings(stdSettings("zio-opencensus"))
.settings(libraryDependencies := Dependencies.opencensus)

lazy val opentracingExample =
project
.in(file("opentracing-example"))
Expand Down
21 changes: 21 additions & 0 deletions opencensus/src/main/scala/zio/telemetry/opencensus/Attribute.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package zio.telemetry.opencensus

import io.opencensus.trace.AttributeValue

object Attributes {
trait implicits {
import scala.language.implicitConversions

implicit def boolToAttribute(b: Boolean): AttributeValue =
AttributeValue.booleanAttributeValue(b)

implicit def stringToAttribute(s: String): AttributeValue =
AttributeValue.stringAttributeValue(s)

implicit def longToAttribute(l: Long): AttributeValue =
AttributeValue.longAttributeValue(l)

implicit def doubleToAttribute(d: Double): AttributeValue =
AttributeValue.doubleAttributeValue(d)
}
}
147 changes: 147 additions & 0 deletions opencensus/src/main/scala/zio/telemetry/opencensus/Live.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package zio.telemetry.opencensus

import zio._

import io.opencensus.trace.AttributeValue
import io.opencensus.trace.BlankSpan
import io.opencensus.trace.Span
import io.opencensus.trace.SpanContext
import io.opencensus.trace.Status
import io.opencensus.trace.propagation.TextFormat
import io.opencensus.trace.Tracer

object Live {
val live: URLayer[Has[Tracer], Tracing] =
ZLayer.fromManaged(for {
tracer <- ZIO.access[Has[Tracer]](_.get).toManaged_
tracing = FiberRef.make[Span](BlankSpan.INSTANCE).map(new Live(tracer, _))
managed <- ZManaged.make(tracing)(_.end)
} yield managed)
}

class Live(tracer: Tracer, root: FiberRef[Span]) extends Tracing.Service {
val currentSpan_ = root

def currentSpan: UIO[Span] = currentSpan_.get

def span[R, E, A](
name: String,
kind: Span.Kind = Span.Kind.SERVER,
toErrorStatus: ErrorMapper[E],
attributes: Map[String, AttributeValue]
)(effect: ZIO[R, E, A]): ZIO[R, E, A] =
for {
parent <- currentSpan_.get
res <- createSpan(parent, name, kind).use(
finalizeSpanUsingEffect(
putAttributes(attributes) *> effect,
toErrorStatus
)
)
} yield res

def root[R, E, A](
name: String,
kind: Span.Kind,
toErrorStatus: ErrorMapper[E],
attributes: Map[String, AttributeValue]
)(effect: ZIO[R, E, A]): ZIO[R, E, A] =
for {
res <- createSpan(BlankSpan.INSTANCE, name, kind).use(
finalizeSpanUsingEffect(
putAttributes(attributes) *> effect,
toErrorStatus
)
)
} yield res

def fromRemoteSpan[R, E, A](
remote: SpanContext,
name: String,
kind: Span.Kind,
toErrorStatus: ErrorMapper[E],
attributes: Map[String, AttributeValue]
)(effect: ZIO[R, E, A]): ZIO[R, E, A] =
for {
res <- createSpanFromRemote(remote, name, kind).use(
finalizeSpanUsingEffect(
putAttributes(attributes) *> effect,
toErrorStatus
)
)
} yield res

def putAttributes(
attributes: Map[String, AttributeValue]
): ZIO[Any, Nothing, Unit] =
for {
current <- currentSpan_.get
_ <- UIO(attributes.foreach({
case ((k, v)) => current.putAttribute(k, v)
}))
} yield ()

private def createSpan(
parent: Span,
name: String,
kind: Span.Kind
): UManaged[Span] =
ZManaged.make(
UIO(
tracer
.spanBuilderWithExplicitParent(name, parent)
.setSpanKind(kind)
.startSpan()
)
)(span => UIO(span.end))

private def createSpanFromRemote(
parent: SpanContext,
name: String,
kind: Span.Kind
): UManaged[Span] =
ZManaged.make(
UIO(
tracer
.spanBuilderWithRemoteParent(name, parent)
.setSpanKind(kind)
.startSpan()
)
)(span => UIO(span.end))

private def finalizeSpanUsingEffect[R, E, A](
effect: ZIO[R, E, A],
toErrorStatus: ErrorMapper[E]
)(span: Span): ZIO[R, E, A] =
for {
r <- currentSpan_
.locally(span)(effect)
.tapCause(setErrorStatus(span, _, toErrorStatus))
} yield r

def inject[C, R, E, A](
format: TextFormat,
carrier: C,
setter: TextFormat.Setter[C]
): UIO[Unit] =
for {
current <- currentSpan
_ <- URIO(format.inject(current.getContext(), carrier, setter))
} yield ()

private[opencensus] def end: UIO[Unit] =
for {
span <- currentSpan_.get
_ <- UIO(span.end())
} yield ()

private def setErrorStatus[E](
span: Span,
cause: Cause[E],
toErrorStatus: ErrorMapper[E]
): UIO[Unit] = {
val errorStatus =
cause.failureOption.flatMap(toErrorStatus.lift).getOrElse(Status.UNKNOWN)
UIO(span.setStatus(errorStatus))
}
}
108 changes: 108 additions & 0 deletions opencensus/src/main/scala/zio/telemetry/opencensus/Tracing.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package zio.telemetry.opencensus

import zio._

import io.opencensus.trace.Span
import io.opencensus.trace.AttributeValue
import io.opencensus.trace.SpanContext
import io.opencensus.trace.propagation.TextFormat

object Tracing {
def defaultMapper[E]: ErrorMapper[E] = Map.empty

trait Service {
def span[R, E, A](
name: String,
kind: Span.Kind = null,
toErrorStatus: ErrorMapper[E] = defaultMapper[E],
attributes: Map[String, AttributeValue] = Map()
)(effect: ZIO[R, E, A]): ZIO[R, E, A]

def root[R, E, A](
name: String,
kind: Span.Kind = null,
toErrorStatus: ErrorMapper[E] = defaultMapper[E],
attributes: Map[String, AttributeValue] = Map()
)(effect: ZIO[R, E, A]): ZIO[R, E, A]

def fromRemoteSpan[R, E, A](
remote: SpanContext,
name: String,
kind: Span.Kind = Span.Kind.SERVER,
toErrorStatus: ErrorMapper[E] = defaultMapper[E],
attributes: Map[String, AttributeValue] = Map()
)(effect: ZIO[R, E, A]): ZIO[R, E, A]

def inject[C, R, E, A](
format: TextFormat,
carrier: C,
setter: TextFormat.Setter[C]
): UIO[Unit]

def putAttributes(
attrs: Map[String, AttributeValue]
): ZIO[Any, Nothing, Unit]

private[opencensus] def end: UIO[Unit]
}

def span[R, E, A](
name: String,
kind: Span.Kind = null,
toErrorStatus: ErrorMapper[E] = defaultMapper[E],
attributes: Map[String, AttributeValue] = Map()
)(effect: ZIO[R, E, A]): ZIO[R with Tracing, E, A] =
ZIO.accessM(_.get.span(name, kind, toErrorStatus, attributes)(effect))

def root[R, E, A](
name: String,
kind: Span.Kind = null,
toErrorStatus: ErrorMapper[E] = defaultMapper[E],
attributes: Map[String, AttributeValue] = Map()
)(effect: ZIO[R, E, A]): ZIO[R with Tracing, E, A] =
ZIO.accessM(_.get.root(name, kind, toErrorStatus, attributes)(effect))

def fromRemoteSpan[R, E, A](
remote: SpanContext,
name: String,
kind: Span.Kind = null,
toErrorStatus: ErrorMapper[E] = defaultMapper[E],
attributes: Map[String, AttributeValue] = Map()
)(effect: ZIO[R, E, A]): ZIO[R with Tracing, E, A] =
ZIO.accessM(
_.get.fromRemoteSpan(remote, name, kind, toErrorStatus, attributes)(
effect
)
)

def putAttributes(
attributes: (String, AttributeValue)*
): ZIO[Tracing, Nothing, Unit] =
ZIO.accessM(_.get.putAttributes(attributes.toMap))

def withAttributes[R, E, A](
attributes: (String, AttributeValue)*
)(eff: ZIO[R, E, A]): ZIO[R with Tracing, E, A] =
ZIO.accessM[Tracing](_.get.putAttributes(attributes.toMap)) *> eff

def fromRootSpan[C, R, E, A](
format: TextFormat,
carrier: C,
getter: TextFormat.Getter[C],
name: String,
kind: Span.Kind = Span.Kind.SERVER,
toErrorStatus: ErrorMapper[E] = defaultMapper[E],
attributes: Map[String, AttributeValue] = Map()
)(effect: ZIO[R, E, A]): ZIO[R with Tracing, E, A] =
Task(format.extract(carrier, getter)).foldM(
_ => root(name, kind, toErrorStatus)(effect),
remote => fromRemoteSpan(remote, name, kind, toErrorStatus, attributes)(effect)
)

def inject[C, R, E, A](
format: TextFormat,
carrier: C,
setter: TextFormat.Setter[C]
): URIO[R with Tracing, Unit] =
ZIO.accessM(_.get.inject(format, carrier, setter))
}
11 changes: 11 additions & 0 deletions opencensus/src/main/scala/zio/telemetry/opencensus/package.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package zio.telemetry

import io.opencensus.trace.Status

import zio.Has
package object opencensus {
object implicits extends Attributes.implicits

type Tracing = Has[Tracing.Service]
type ErrorMapper[E] = PartialFunction[E, Status]
}
41 changes: 41 additions & 0 deletions opencensus/src/main/scala/zio/telemetry/opencensus/syntax.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package zio.telemetry.opencensus

import zio._

import io.opencensus.trace.AttributeValue
import io.opencensus.trace.SpanContext
import io.opencensus.trace.Span

object syntax {
implicit final class OpenCensusZioOps[R, E, A](val effect: ZIO[R, E, A]) extends AnyVal {
def span(
name: String,
kind: Span.Kind = null,
toErrorStatus: ErrorMapper[E] = Tracing.defaultMapper[E],
attributes: Map[String, AttributeValue]
): ZIO[R with Tracing, E, A] =
Tracing.span(name, kind, toErrorStatus, attributes)(effect)

def root(
name: String,
kind: Span.Kind = null,
toErrorStatus: ErrorMapper[E] = Tracing.defaultMapper[E],
attributes: Map[String, AttributeValue]
): ZIO[R with Tracing, E, A] =
Tracing.root(name, kind, toErrorStatus, attributes)(effect)

def fromRemoteSpan(
remote: SpanContext,
name: String,
kind: Span.Kind,
toErrorStatus: ErrorMapper[E],
attributes: Map[String, AttributeValue]
): ZIO[R with Tracing, E, A] =
Tracing.fromRemoteSpan(remote, name, kind, toErrorStatus, attributes)(effect)

def withAttributes(
attributes: (String, AttributeValue)*
): ZIO[R with Tracing, E, A] =
Tracing.withAttributes(attributes: _*)(effect)
}
}
7 changes: 7 additions & 0 deletions project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ object Dependencies {
val sttp = "2.1.1"
val opentracing = "0.33.0"
val opentelemetry = "0.3.0"
val opencensus = "0.26.0"
val zipkin = "2.15.0"
val zio = "1.0.0-RC19-2"
}
Expand All @@ -30,6 +31,12 @@ object Dependencies {
"org.scala-lang.modules" %% "scala-collection-compat" % "2.1.6"
)

lazy val opencensus = zio ++ Seq(
"io.opencensus" % "opencensus-api" % Versions.opencensus,
"io.opencensus" % "opencensus-impl" % Versions.opencensus,
"io.opencensus" % "opencensus-contrib-http-util" % Versions.opencensus
)

lazy val example = Seq(
"org.typelevel" %% "cats-core" % "2.1.1",
"io.circe" %% "circe-generic" % "0.13.0",
Expand Down

0 comments on commit 4b3b2df

Please sign in to comment.