Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Activity
    λoλcat
    @catostrophe:matrix.org
    [m]
    tdbgamer (Ti imothy Bess) I did it and it seemed to work. But I don't have access to that project anymore
    Jarrod Young
    @jarrodcodes
    We are having an issue with getting a span down to where it's needed. Their service needs a Dispatcher, but we only have a Dispatcher[IO]. We need a traced Dispatcher or a way to pass a Span down that's not a noop
    right now the code is def tracedDispatcher[F[_]: Applicative]( dispatcher: Dispatcher[F] ): Dispatcher[Kleisli[F, Span[F], *]] = new Dispatcher[Kleisli[F, Span[F], *]] { override def unsafeToFutureCancelable[A]( fa: Kleisli[F, Span[F], A] ): (Future[A], () => Future[Unit]) = dispatcher.unsafeToFutureCancelable(fa.run(Span.noopInstance)) }
    which kills the tracing
    we want a real span, but this is constructed before a span context because it's for the Dispatcher
    fiadliel
    @fiadliel:matrix.org
    [m]
    Hey, I'm looking at the tapir tracing code (integrating with http4s), and one thing I'm interested in is adding as much context as possible (e.g. headers, et al). It seems clunky right now to, for example, add headers to tracing at the tapir level. So I'm playing with using both the http4s tracer and the tapir tracer (http4s adds a span with basic HTTP info, and tapir adds the route-specific info).

    For linking the two, I've hacked together some code that looks like

      def addTrace[I, E, O, R](
          serverEndpoint: ServerEndpoint[I, E, O, R, Kleisli[F, Span[F], _]]
      ): ServerEndpoint[I, E, O, R, Kleisli[F, Span[F], _]] =
        val inputSpanNamer =
          TapirSpanNamer.methodWithPathTemplate[I](serverEndpoint.endpoint, _)
    
        serverEndpoint.copy(logic =
          (MEF: sttp.monad.MonadError[Kleisli[F, Span[F], _]]) =>
            (input: I) =>
              Trace[Kleisli[F, Span[F], _]]
                .span(inputSpanNamer(input))(serverEndpoint.logic(MEF)(input))
        )

    Does this kind of thing look reasonable? Or the wrong way to do this? Should this kind of call be part of the library?

    (the above call being used to inject a new span in tapir where the tracing context was already set up in the http4s context)
    fiadliel
    @fiadliel:matrix.org
    [m]
    And as a new visitor here, thanks a lot for the great library! It fills a big hole in the Scala tracing ecosystem.
    Henry
    @hygt
    @catostrophe @janstenpickle hi it's me again :smile: so I could finally migrate some projects at work from Kamon to t4c and I'm using the Zipkin exporter at last, which made me want to contribute again as I noticed a few things:
    • I think the Zipkin exporter should not depend on the Blaze http4s client, Ember is becoming the recommended default and we should just take a Client[F] (we already have apply methods that do just that)
    • is there a reason all the constructors in HttpSpanExporter take a uri: String instead of a org.http4s.Uri directly?
    • would you be OK with a thrift-over-HTTP module in the jaeger integration as an alternative to the current UDP exporter? I will need that at work (we're finally moving from Zipkin to Jaeger collectors)
    catostrophe
    @catostrophe
    @hygt feel free to open issues and PRs in the corresponding repos
    1) I'm fine with switching to Ember if it's mature enough, let's do it for all clients
    2) same here: let's do it, but in all clients where applicable
    3) have you tried trace4cats-opentelemetry?
    catostrophe
    @catostrophe
    We used thrift-over-udp at $WORK but then switched to grpc
    Henry
    @hygt
    regarding the client it seems Ember is reasonably mature now, however my point was more that t4c should not bring any actual client implementation as a dependency, since depending on http4s-client is enough (trace4cats-jaeger-integration-test depends on Blaze but that's fine in test dependencies)
    λoλcat
    @catostrophe:matrix.org
    [m]
    Ah. Agree
    Henry
    @hygt
    and yes I've seen the otlp json-over-http exporter but our infra people aren't interested in that :confused: for now we can still use Zipkin if we want JSON spans but that's it, they want us to migrate to Jaeger formats (either via a Jaeger agent or without)
    anyway I'll open PRs :smiley:
    Henry
    @hygt
    by the way you guys aren't moving to Discord? a channel on the Typelevel server would be convenient (natchez has one there)
    34 replies
    catostrophe
    @catostrophe
    @hygt thanks! Will look at it tomorrow!
    catostrophe
    @catostrophe
    @hygt just merged your PRs
    catostrophe
    @catostrophe
    @fiadliel:matrix.org @janstenpickle Hey! Who wants to discuss the issues with the implementation of traced secured server endpoints? trace4cats/trace4cats-sttp#82
    mpichette-apple
    @mpichette-apple
    Hi there! Thanks for a great lib! A quickie to start with: are the SemanticAttributeKeys meant to be compatible with OpenTelemetry? And would PRs to add a few be accepted? I would like to add more attributes to the Http4sClient integration as right now spans lack most attributes...
    11 replies
    catostrophe
    @catostrophe
    @/all trace4cats/trace4cats#662 just opened an issue to track what we can do before the next release.
    Alex
    @unthingable
    is there a way to add request headers in otlp-http-exporter?
    Alex
    @unthingable
    To get a Provide instance that works with Trace.Implicits.noop do I need to write one myself or is there a more elegant way?
    Calvin Lee Fernandes
    @calvinlfer

    Hey all - does the new-relic exporter work? - i tried to check out the inject-zio example and use the New Relic entrypoint with my API (License) key but I don't seem to be getting any trace data in the UI. I used the following API:

      def entryPoint[F[_]: Async](process: TraceProcess): Resource[F, EntryPoint[F]] = {
        for {
          client <- BlazeClientBuilder[F].resource
          completer <- NewRelicSpanCompleter[F](
            client = RequestLogger[F](
              logHeaders = true,
              logBody = true,
              logAction = Some(i => Sync[F].delay(println(s"Request: $i")))
            )(
              ResponseLogger[F](
                logHeaders = true,
                logBody = true,
                logAction = Some(i => Sync[F].delay(println(s"Response: $i")))
              )(client)
            ),
            process = process,
            apiKey = "my-new-relic-key-went-in-here",
            endpoint = io.janstenpickle.trace4cats.newrelic.Endpoint.US
          )
        } yield EntryPoint[F](SpanSampler.always[F], completer)
      }

    apologies in advance if i'm doing something silly

    I see that the requests are being made successfully to new relic (i'm getting back 202 Accepted)
    i'm using the same key for Kamon's Tracing API and it seems to be working alright over there - but not sure if I need to do any additional setup on NR for trace4cats
    Calvin Lee Fernandes
    @calvinlfer
    figured it out - slight problem in the payload sent to new-relic will submit an MR :D
    yippee
    image.png
    (i adapted the example but the original code is still doing the right hing)
    Calvin Lee Fernandes
    @calvinlfer
    PRs up for u @catostrophe @janstenpickle whenever you have a moment: trace4cats/trace4cats-newrelic#66
    Thanks in advance :pray:
    catostrophe
    @catostrophe
    @calvinlfer thanks. I promise to find some time this week to merge all pending PRs and do a mass release
    Calvin Lee Fernandes
    @calvinlfer
    Sweet! thank you my friend!
    Alex
    @unthingable

    We have a bit of a problem with OTLP exporters, in that their write method tends to look like this:

          spans
                .foldLeft(ListBuffer.empty[SpanData]) { (buf, span) =>
                  buf += Trace4CatsSpanData(Trace4CatsResource(span.serviceName), span)

    This is quite spartan and our problem is that we need the resource to also include deployment.environment in addition to service.name. Is there a recommended way to get additional resource attributes without forking and hacking?

    Calvin Lee Fernandes
    @calvinlfer
    hey @catostrophe and @janstenpickle (and any others) - I had a use case at work where a client of mine uses ZIO directly and doesn't use Tagless Final. I managed to improve the ergonomics and usage so I can use parts of the ZIO environment and ZLayers and have Trace4Cats spans work well together. I would really appreciate if you had some time to take a look:
    trace4cats/trace4cats-zio#68
    catostrophe
    @catostrophe
    Sure, will check. The original t4c-zio integration relied on ZIO as a backend IO runtime for the tagless final code. So we didn't use ZLayer features.
    Calvin Lee Fernandes
    @calvinlfer
    awesome, thank you! I still wanted to preserve that existing functionality so users could still keep using Tagless and commit to ZIO right at the end, so all the existing http4s-zio examples using tagless still work :D
    Calvin Lee Fernandes
    @calvinlfer
    hey @catostrophe i realized i broke some functionality for tagless final users when it came to lenses with this approach so i kept the old implementation without using ZLayers
    some of the tests related to that were failing
    image.png
    so i kept spannedRIOTrace around
    Calvin Lee Fernandes
    @calvinlfer
    pushed my changes
    Calvin Lee Fernandes
    @calvinlfer
    I have a conceptual question - when processing messages from a traced Kafka consumer, each record is associated with a Span/TraceHeader. If I want to process a set of messages in a batch (using something like groupWithin(x elements, y seconds)) what would be the best way to indicate that all messages have been processed to the tracing system?
    For now, I ended up creating a new root span and have linked all message spans to the root span to indicate that the individual messages have been processed but it seems like New Relic does not really support the links. I was wondering if you folks had some suggestions :D
    Calvin Lee Fernandes
    @calvinlfer
    hey @catostrophe sorry to bother you again - do you think you'll have some time this week to review the interop changes to ZIO?
    catostrophe
    @catostrophe
    @calvinlfer I will try. Sorry for a long review. Can't find a spare hour...
    Calvin Lee Fernandes
    @calvinlfer
    no worries @catostrophe - im really sorry to bug you - thank you so much for using your spare time for this <3
    Gavin Bisesi
    @Daenyth
    if I do Trace[F].span("outer") { Trace[F].span("inner") { someFA } }, is inner marked as a child of outer
    Oleksandr Shevchenko
    @o-shevchenko

    Hi
    I'm trying to add integration with Jaeger to my Tapir application with Blaze http4s backend.
    I managed how to create entryPoint and send span to Jaeger, but my endpoints still don't produce B3 headers and don't reuse parent context from another microservice (written on Micronaut Kotlin).
    I can't understand how to use client.liftTrace() with my Tapir ServerEndpoint.

    object Server extends ResourceApp.Forever with Metrics with Tracing {
    
      def makeRoutes[F[_]: Async](client: Client[F]): HttpRoutes[F] = {
        val impl = new Implementation[F]
        val serverLogic: List[ServerEndpoint[Any, F]] = List(
          Endpoints.list.serverLogic(impl.list),
          Endpoints.test.serverLogic(impl.test),
          Endpoints.healthz.serverLogic(_ => impl.healthz))
    
        val serverOptions = Http4sServerOptions
          .customInterceptors[F, F]
          .exceptionHandler(CustomExceptionHandler.handler)
          .options
    
        Http4sServerInterpreter[F](serverOptions).toRoutes(serverLogic  ++ metricsEndpoints)
      }
    
      override def run(args: List[String]): Resource[IO, Unit] = {
    
        type F[x] = IO[x]
        type G[x] = Kleisli[F, Span[F], x]
        for {
          client <- BlazeClientBuilder[F].resource
          ep <- entryPoint[F](client, TraceProcess("browser"))
    
          routes = makeRoutes[G](client.liftTrace(ToHeaders.b3))
          _ <-
            BlazeServerBuilder[F]
              .bindHttp(conf.port, conf.host)
              .withHttpApp(routes.inject(ep, requestFilter = Http4sRequestFilter.fullPaths("/api/v1/healthz", "/api/v1/metrics")).orNotFound)
              .resource
        } yield ()
      }
    }
    private[rest] trait Tracing {
      def entryPoint[F[_]: Async](client: Client[F], process: TraceProcess): Resource[F, EntryPoint[F]]  =
          JaegerHttpSpanCompleter[F](
            client,
            process,
            "jaeger-collector",
            14268,
            config = CompleterConfig(batchTimeout = 50.millis)).map { completer =>
            EntryPoint[F](SpanSampler.probabilistic[F](1), completer, ToHeaders.b3)
          }
    
    }

    Could someone help me to figure out how to work with B3 headers?
    I think that's the missing part:

    HttpRoutes.of { case req @ GET -> Root / "forward" =>
          client.expect[String](req).flatMap(Ok(_))
        }

    that's from http4s example, but there isn't any example how to work with Tapir ServerEndpoints

    3 replies
    joriscode
    @joriscode

    Hi, I'm experimenting tracing FS2 streams and I'm reaching out to have a feedback on my approach.
    I defined the type alias TracedPipe and few extension methods to hide away the fact that the stream is lifted to TracedStream: collect, evalTap, throughNotTraced.
    I wonder if I'm on the right tracks because it seems that I'll have to define and test a lot of the FS2 API myself; which does not seem to be the right approach and error-prone.
    So, Am I missing something out here?

    Moreover:

    • the methods don't generate a new spans yet but this will lead to defined even more signatures!
    • I've left out the implementation of more tricky methods such as evalScan that are even more likely to implemented incorrectly (by me)

    Current (simplified) example from my codebase:

    def process[F[_]: Async](serviceA: ServiceA[F]): Pipe[F, Event, Action] =
        _
            .collect { case event if event.isEnabled => event.request }
            .through(monitor)
              .through(handle(serviceA))
              .map { case response => Action(response) }
    
    private def handle[F[_]: Async](serviceA: ServiceA[F]): Pipe[F, Request, Response] =
        _
            .evalTap(log.debug("debug"))
            .mapAsyncUnordered() { case request => Response.from(request) }
    
    private def monitor[F[_], T]: Pipe[F, T, T] =
        ???

    Traced approach of the above example:

    def process[F[_]: Async](serviceA: ServiceA[F]): Pipe[F, Event, Action] =
        _
            .inject(ep, "root-span")
            .collect { case event if event.isEnabled => event.request } // defined as extension method
            .throughNotTraced(monitor) // defined as extension method
              .through(handle(serviceA))
              .map { case response => Action(response) }
              .run
    
    
    // defined TracedPipe alias
    type TracedPipe[F[_], I, O] = TracedStream[F, I] => TracedStream[F, O]
    
    private def handle[F[_]: Async](serviceA: ServiceA[F]): TracedPipe[F, Request, Response] =
        _
            .evalTap(log.debug("debug")) // defined as extension method
            .mapAsyncUnordered() { case request => Response.from(request) } // defined as extension method
    
    // unchanged
    private def monitor[F[_], T]: Pipe[F, T, T] =
        ???