- Notifications
You must be signed in to change notification settings - Fork6
Common interface for tracing/instrumentation libraries in OCaml
c-cube/ocaml-trace
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
This small library provides basic types that can be used to instrumenta library or application, either by hand or via a ppx.
- spans
- messages
- counters
- other metrics?
- ppx to help instrumentation
To instrument your code, you can simply addtrace
to your dune/opam files, and thenwrite code like such:
letfx=Trace.with_span ~__FILE__ ~__LINE__"inside-f"@@fun_sp ->(* … code for f*)letgx=Trace.with_span ~__FILE__ ~__LINE__"inside-g"@@fun_sp ->let y= f xin(* … code for f*)let()=Some_trace_backend.setup()@@fun() ->let result= g42in print_result result
The filetest/t1.ml
follows this pattern, usingtrace-tef
as a simple backendthat emits one JSON object per span/message:
letrun()=Trace.set_process_name"main";Trace.set_thread_name"t1";let n=ref0infor _i=1to50doTrace.with_span ~__FILE__ ~__LINE__"outer.loop"@@fun_sp ->for _j=2to5do incr n;Trace.with_span ~__FILE__ ~__LINE__"inner.loop"@@fun_sp ->Trace.messagef (funk -> k"hello %d %d" _i _j);Trace.message"world";Trace.counter_int"n"!n done donelet()=Trace_tef.with_setup~out:(`File"trace.json")()@@fun() -> run()
After running this, the file "trace.json" will contain something like:
[{"pid":2,"name":"process_name","ph":"M","args": {"name":"main"}},{"pid":2,"tid":3,"name":"thread_name","ph":"M","args": {"name":"t1"}},{"pid":2,"cat":"","tid":3,"ts":2.00,"name":"hello 1 2","ph":"I"},{"pid":2,"cat":"","tid":3,"ts":3.00,"name":"world","ph":"I"},{"pid":2,"tid":3,"ts":4.00,"name":"c","ph":"C","args": {"n":1}},…
Opening it inhttps://ui.perfetto.dev we get something like this:
On OCaml >= 4.12, and withppxlib
installed, you can installppx_trace
.This is a preprocessor that will rewrite like so:
let%trace f x y z= do_sth x; do_sth y;beginlet%trace()="sub-span"in do_sth zend
This more or less corresponds to:
letfxyz=let _trace_span=Trace_core.enter_span ~__FILE__ ~__LINE__"Foo.f"inmatch do_sth x; do_sth y;beginlet _trace_span=Trace_core.enter_span ~__FILE__ ~__LINE__"sub-span"inmatch do_sth zwith|res ->Trace_core.exit_span _trace_span; res|exceptione ->Trace_core.exit_span _trace_span raise eend;with|res ->Trace_core.exit_span _trace_span res|exceptione ->Trace_core.exit_span _trace_span raise e
Alternatively, a name can be provided for the span, which is useful if you wantto access it and use functions likeTrace.add_data_to_span
:
let%trace f x y z= do_sth x; do_sth y;beginlet%trace _sp="sub-span"in do_sth z;Trace.add_data_to_span _sp ["x",`Int42]end
In yourlibrary
orexecutable
stanza, add:(preprocess (pps ppx_trace))
.The dependency ontrace.core
is automatically added. You still need toconfigure a backend to actually do collection.
Concrete tracing or observability formats such as:
- Fuchsia (seethe spec andtracing.Can be opened inhttps://ui.perfetto.dev)
- Catapult
- light bindings here with
trace-tef
.(Can be opened inhttps://ui.perfetto.dev) - backend fortldrs, asmall rust daemon that aggregates TEF traces from multiple processes/clientsinto a single
.jsonl
file - tldrs, to collect TEF traces from multiple processes in a clean way.This requires the rust
tldrs
program to be in path. [ ] richer bindings withocaml-catapult,with multi-process backends, etc.(subsumed by tldrs)
- light bindings here with
- Tracy (seeocaml-tracy, more specifically
tracy-client.trace
) - Opentelemetry (seeocaml-opentelemetry, in
opentelemetry.trace
) - landmarks?
- Logs (only for messages, obviously)
The librarytrace.subscriber
defines composablesubscribers, which are sets of callbacksthat consume tracing events.Multiple subscribers can be aggregated together (with events being dispatched to all of them)and be installed as a normalcollector.
About
Common interface for tracing/instrumentation libraries in OCaml