Skip to content

Commit

Permalink
Merge pull request #3 from davenverse/namesAndKind
Browse files Browse the repository at this point in the history
Add Kind, and Custom Span Name Function
  • Loading branch information
ChristopherDavenport authored Apr 19, 2022
2 parents e7ac799 + e178453 commit e5718e0
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 21 deletions.
4 changes: 2 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ThisBuild / tlBaseVersion := "0.1" // your current series x.y
ThisBuild / tlBaseVersion := "0.2" // your current series x.y

ThisBuild / organization := "io.chrisdavenport"
ThisBuild / organizationName := "Christopher Davenport"
Expand All @@ -24,7 +24,7 @@ val catsV = "2.7.0"
val catsEffectV = "3.3.11"
val fs2V = "3.2.7"
val http4sV = "0.23.7"
val natchezV = "0.1.4"
val natchezV = "0.1.6"
val munitCatsEffectV = "1.0.7"

val slf4jV = "1.7.30"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,17 @@ object ClientMiddleware {
def trace[F[_]: natchez.Trace: MonadCancelThrow](
ep: EntryPoint[F], // This is to escape from F Trace to Resource[F, *] timing. Which is critical
reqHeaders: Set[CIString] = OTHttpTags.Headers.defaultHeadersIncluded,
respHeaders: Set[CIString] = OTHttpTags.Headers.defaultHeadersIncluded
respHeaders: Set[CIString] = OTHttpTags.Headers.defaultHeadersIncluded,
clientSpanName: Request[F] => String = {(req: Request[F]) => s"Http Client - ${req.method}"},
additionalRequestTags: Request[F] => Seq[(String, TraceValue)] = {(_: Request[F]) => Seq()},
additionalResponseTags: Response[F] => Seq[(String, TraceValue)] = {(_: Response[F]) => Seq()},
)(client: Client[F]): Client[F] =
Client[F]{(req: Request[F]) =>
val base = request(req, reqHeaders)
val base = request(req, reqHeaders) ++ additionalRequestTags(req)
MonadCancelThrow[Resource[F, *]].uncancelable(poll =>
for {
baggage <- Resource.eval(Trace[F].kernel)
span <- ep.continueOrElseRoot(req.uri.path.toString, baggage)
span <- ep.continueOrElseRoot(clientSpanName(req), baggage)
_ <- Resource.eval(span.put(base:_*))
knl <- Resource.eval(span.kernel)
knlHeaders = Headers(knl.toHeaders.map { case (k, v) => Header.Raw(CIString(k), v) } .toSeq)
Expand All @@ -33,15 +36,14 @@ object ClientMiddleware {
Resource.eval(span.put("exit.case" -> "succeeded")) >>
fa.flatMap(resp =>
Resource.eval(
span.put(response(resp, respHeaders):_*)
span.put((response(resp, respHeaders) ++ additionalResponseTags(resp)):_*)
)
)
case Outcome.Errored(e) =>
val exitCase: (String, TraceValue) = ("exit.case" -> TraceValue.stringToTraceValue("errored"))
val error = OTHttpTags.Errors.error(e)
Resource.eval(
span.put(exitCase) >>
span.put(error:_*)
span.put((exitCase :: error):_*)
)
case Outcome.Canceled() =>
// Canceled isn't always an error, but it generally is for http
Expand All @@ -62,6 +64,7 @@ object ClientMiddleware {

def request[F[_]](request: Request[F], headers: Set[CIString]): List[(String, TraceValue)] = {
val builder = new ListBuffer[(String, TraceValue)]()
builder += OTHttpTags.Common.kind("client")
builder += OTHttpTags.Common.method(request.method)
builder += OTHttpTags.Common.url(request.uri)
builder += OTHttpTags.Common.target(request.uri)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import io.chrisdavenport.natchezhttp4sotel.helpers.printStackTrace

object OTHttpTags {
object Common {
def kind(kind: String): (String, TraceValue) = ("span.kind", kind)
def method(m: Method): (String, TraceValue) = ("http.method", m.name)
def url(url: Uri): (String, TraceValue) = ("http.url", url.renderString)
def target(url: Uri): (String, TraceValue) = ("http.target", url.copy(scheme = None, authority = None).renderString)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,29 @@ object ServerMiddleware {
ep: EntryPoint[F],
isKernelHeader: CIString => Boolean = name => !ExcludedHeaders.contains(name),
reqHeaders: Set[CIString] = OTHttpTags.Headers.defaultHeadersIncluded,
respHeaders: Set[CIString] = OTHttpTags.Headers.defaultHeadersIncluded
respHeaders: Set[CIString] = OTHttpTags.Headers.defaultHeadersIncluded,
routeClassifier: Request[F] => Option[String] = {(_: Request[F]) => None},
serverSpanName: Request[F] => String = {(req: Request[F]) => s"Http Server - ${req.method}"},
additionalRequestTags: Request[F] => Seq[(String, TraceValue)] = {(_: Request[F]) => Seq()},
additionalResponseTags: Response[F] => Seq[(String, TraceValue)] = {(_: Response[F]) => Seq()},
)(f: Trace[F] => HttpApp[F]): HttpApp[F] =
MakeSureYouKnowWhatYouAreDoing.tracedF(ep, FunctionK.id[F], isKernelHeader, reqHeaders, respHeaders)(f.andThen(_.pure[F]))
MakeSureYouKnowWhatYouAreDoing.tracedF(ep, FunctionK.id[F], isKernelHeader, reqHeaders, respHeaders,
routeClassifier, serverSpanName, additionalRequestTags, additionalResponseTags
)(f.andThen(_.pure[F]))

def httpRoutes[F[_]: MonadCancelThrow: GenFiberLocal](
ep: EntryPoint[F],
isKernelHeader: CIString => Boolean = name => !ExcludedHeaders.contains(name),
reqHeaders: Set[CIString] = OTHttpTags.Headers.defaultHeadersIncluded,
respHeaders: Set[CIString] = OTHttpTags.Headers.defaultHeadersIncluded
respHeaders: Set[CIString] = OTHttpTags.Headers.defaultHeadersIncluded,
routeClassifier: Request[F] => Option[String] = {(_: Request[F]) => None},
serverSpanName: Request[F] => String = {(req: Request[F]) => s"Http Server - ${req.method}"},
additionalRequestTags: Request[F] => Seq[(String, TraceValue)] = {(_: Request[F]) => Seq()},
additionalResponseTags: Response[F] => Seq[(String, TraceValue)] = {(_: Response[F]) => Seq()},
)(f: Trace[F] => HttpRoutes[F]): HttpRoutes[F] =
MakeSureYouKnowWhatYouAreDoing.tracedF(ep, OptionT.liftK, isKernelHeader, reqHeaders, respHeaders)(f.andThen(_.pure[OptionT[F, *]]))
MakeSureYouKnowWhatYouAreDoing.tracedF(ep, OptionT.liftK, isKernelHeader, reqHeaders, respHeaders,
routeClassifier, serverSpanName, additionalRequestTags, additionalResponseTags
)(f.andThen(_.pure[OptionT[F, *]]))

object MakeSureYouKnowWhatYouAreDoing {
// This effect to generate routes will run on every request.
Expand All @@ -45,7 +57,10 @@ object ServerMiddleware {
isKernelHeader: CIString => Boolean = name => !ExcludedHeaders.contains(name),
reqHeaders: Set[CIString] = OTHttpTags.Headers.defaultHeadersIncluded,
respHeaders: Set[CIString] = OTHttpTags.Headers.defaultHeadersIncluded,
routeClassifier: Request[F] => Option[String] = {(_: Request[F]) => None}
routeClassifier: Request[F] => Option[String] = {(_: Request[F]) => None},
serverSpanName: Request[F] => String = {(req: Request[F]) => s"Http Server - ${req.method}"},
additionalRequestTags: Request[F] => Seq[(String, TraceValue)] = {(_: Request[F]) => Seq()},
additionalResponseTags: Response[F] => Seq[(String, TraceValue)] = {(_: Response[F]) => Seq()},
)(
f: Trace[F] => G[Http[G, F]]
): Http[G, F] = cats.data.Kleisli{(req: Request[F]) =>
Expand All @@ -58,16 +73,16 @@ object ServerMiddleware {
val kernel = Kernel(kernelHeaders)

MonadCancelThrow[G].uncancelable(poll =>
ep.continueOrElseRoot(req.uri.path.toString, kernel).mapK(fk).use{span =>
val init = request(req, reqHeaders, routeClassifier)
ep.continueOrElseRoot(serverSpanName(req), kernel).mapK(fk).use{span =>
val init = request(req, reqHeaders, routeClassifier) ++ additionalRequestTags(req)
fk(span.put(init:_*)) >>
fk(GenFiberLocal[F].local(span)).map(fromFiberLocal(_))
.flatMap( trace =>
poll(f(trace).flatMap(_.run(req))).guaranteeCase{
case Outcome.Succeeded(fa) =>
fk(span.put("exit.case" -> "succeeded")) >>
fa.flatMap{resp =>
val out = response(resp, respHeaders)
val out = response(resp, respHeaders) ++ additionalResponseTags(resp)
fk(span.put(out:_*))
}
case Outcome.Errored(e) =>
Expand All @@ -88,6 +103,7 @@ object ServerMiddleware {

def request[F[_]](request: Request[F], headers: Set[CIString], routeClassifier: Request[F] => Option[String]): List[(String, TraceValue)] = {
val builder = new ListBuffer[(String, TraceValue)]()
builder += OTHttpTags.Common.kind("server")
builder += OTHttpTags.Common.method(request.method)
builder += OTHttpTags.Common.url(request.uri)
builder += OTHttpTags.Common.target(request.uri)
Expand Down
11 changes: 6 additions & 5 deletions examples/src/main/scala/example/Common.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@ trait Common {
}

// Our routes, in abstract F with a Trace constraint.
def routes[F[_]: Trace](client: Client[F])(
implicit ev: MonadError[F, Throwable]
): HttpRoutes[F] = {
def routes[F[_]: Trace: Concurrent](client: Client[F]): HttpRoutes[F] = {
object dsl extends Http4sDsl[F]; import dsl._ // bleh
HttpRoutes.of[F] {

Expand All @@ -36,9 +34,12 @@ trait Common {
res <- Ok(str)
} yield res
case GET -> Root / "client" / "hello" / name =>
client.toHttpApp.run(Request(Method.GET, uri"http://localhost:8080/hello" / name))
client.expect[String](Request[F](Method.GET, uri"http://localhost:8080/hello" / name))
.flatMap(Ok(_))
case GET -> Root / "client" / "proxy" / "hello" / name =>
client.toHttpApp.run(Request[F](Method.GET, uri"http://localhost:8080/client/hello" / name))
case GET -> Root / "fail" =>
ev.raiseError(new RuntimeException("💥 Boom!"))
Concurrent[F].raiseError(new RuntimeException("💥 Boom!"))

}
}
Expand Down

0 comments on commit e5718e0

Please sign in to comment.