Fork me on GitHub
#missionary
<
2023-01-06
>
ognen.ivanovski17:01:04

Hi, Has anyone thought how would one go about adding tracing / observability instrumentations to tasks and flows?

xificurC20:01:52

Do you have a specific use case or library in mind? A single task could be wrapped in a new task that adds tracing support. Here's a mock-up

ognen.ivanovski17:01:01

Not particularly, though eventually I would like to export to an opentelemetry collector (traces initially).

ognen.ivanovski17:01:32

Your answer is right on point and I think it is the right direction. A follow up come to mind (in the context of https://opentelemetry.io/docs/concepts/signals/traces/#spans-in-opentelemetry: Tracing spans can be nested (in a breakdown semantics). For my case — an aggregator / facade kind of a service combining data from dozens of sources, it would be nice if I can propagate the parent span (identifying the incoming request) in the outgoing calls. “Normally” this is done by saving the tracing context in a thread-local variable (you’d do something like (with-tracing context …forms) but with missionary these would be useless. On the other hand there is the nice task composition that should lend itself easily for this kind of instrumentation.. So, given the example above how would one go about finding out some contextual information from the parent task?

ognen.ivanovski17:01:13

just noticed that the context could be passed explicitly via the trace-args in the example above. This could work (passing a context object around) though i’m a bit reluctant to introduce one just for tracing..

xificurC20:01:14

> it would be nice if I can propagate the parent span (identifying the incoming request) in the outgoing calls What do you call "outgoing calls" in your use case? > “Normally” this is done by saving the tracing context in a thread-local variable (you’d do something like (with-tracing context …forms) but with missionary these would be useless Missionary is execution-agnostic and doesn't spawn any threads. For a task to run on a thread the task itself has to start one or you have to call m/via explicitly. > So, given the example above how would one go about finding out some contextual information from the parent task? I'd need to think of a more specific example but again, if you're on the same thread this might just work.

ognen.ivanovski20:01:07

> What do you call “outgoing calls” in your use case? The service accepts incoming requests (via pedestal) and in order satisfy those requests, it needs to call a number (2-10) microservices in the background — those are the “outgoing calls”. Similar (but simpler) to a graphql engine, it will fetch a document first from a cms and then run “resolvers” for parts of the document tree that fetch additional data from other services. In terms of opentracing/opentelemetry, the incoming request would create a “span” for the entire request. I have to send this span as a “parent span” with each outgoing call l so eventually I can get a trace log like the one in the image here. What would best suite this use case is some kind of ThreadLocal analogue but attached to a process (sp or ap). Maybe expressed as a https://github.com/clojure/clojure/blob/clojure-1.10.1/src/clj/clojure/core.clj#L1947-like macro, but again, binding the vars to the process, not to a thread so to keep with execution-agnostic nature of missionary?

xificurC20:01:31

are those microservices called through HTTP? What kind of call are you making, sync or async? Would you wrap the calls in an m/via?

xificurC20:01:50

what http lib are you using?

ognen.ivanovski20:01:05

Through http (in via pedestal/jetty) out via jetty http-client

xificurC20:01:44

can you link me to the jetty http-client javadocs you're using?

xificurC20:01:05

if possible a code sample of your usage would also be beneficial

ognen.ivanovski20:01:57

let me prepare a gist

xificurC21:01:59

Thanks for the gist, I'll try to take a look tomorrow. On a quick glance I don't see the jetty http client in the sample, is that a future wish to have it in there?

ognen.ivanovski21:01:04

Yes, it is a future wish. Also the http-service sin’t there yet but it calls runs fetch-task for every request. Ideally, the caller of fetch-tasksets up the parent span (reads in from the incoming http request or starts a root one. Then the fetch-method implementations would be wrapped with a utility task to pull the span from context and encode it in the outgoing http request … somehow.

ognen.ivanovski21:01:46

Thanks again for your time.

xificurC11:01:58

np, I've spent some time adding opentracing (and later opentelemetry) tracing to some java/groovy projects 😉 I'm not sure if there's any work for you to do here, though. If you're using opentelemetry then you can use https://github.com/open-telemetry/opentelemetry-java-instrumentation/tree/main/instrumentation/jetty-httpclient (or let the java agent auto-instrument it)

xificurC11:01:06

the only place where missionary would lose the trace context would be you using m/via, in which case you just need to reinstate the context

ognen.ivanovski12:01:59

I guess via and the run-resolutions flow which starts the fetch requests in parallel. But yes, you are right, introducing an anv/context in a few spots will do the trick.

xificurC12:01:38

run-resolutions doesn't fork any threads so there should be no need to wrap there, I think. Given you use the existing jetty wrapper you just need a tracing-via macro that will expand to the above pseudocode

steffan09:01:42

I don't have any comment on Missionary specifics, but you may want to look at https://github.com/steffan-westcott/clj-otel, my Clojure API wrapper library of Java OpenTelemetry. Several functions in the library take an optional :context or :parent parameter, which allow a context to be passed in, rather than use the default "current context" ThreadLocal. Async span support is based around the low-level function https://cljdoc.org/d/com.github.steffan-westcott/clj-otel-api/0.1.5/api/steffan-westcott.clj-otel.api.trace.span#async-span. There's an example of its use with core.async in the examples, specifically the <https://github.com/steffan-westcott/clj-otel/blob/02107fe70a4944ad5cceb77f72908ef22aa8dd8a/examples/common-utils/core-async/src/example/common_utils/core_async.clj#L199%7C&lt;with-span-binding> macro.

ognen.ivanovski13:01:32

Thanks a lot, I will definitely look into this!