Page
Library
Module
Module type
Parameter
Class
Class type
Source
This small library provides basic types that can be used to instrument a library or application, either by hand or via a ppx.
To instrument your code, you can simply add trace
to your dune/opam files, and then write code like such:
let f x =
Trace.with_span ~__FILE__ ~__LINE__ "inside-f" @@ fun _sp ->
(* … code for f *)
let g x =
Trace.with_span ~__FILE__ ~__LINE__ "inside-g" @@ fun _sp ->
let y = f x in
(* … code for f *)
let () =
Some_trace_backend.setup () @@ fun () ->
let result = g 42 in
print_result result
The file test/t1.ml
follows this pattern, using trace-tef
as a simple backend that emits one JSON object per span/message:
let run () =
Trace.set_process_name "main";
Trace.set_thread_name "t1";
let n = ref 0 in
for _i = 1 to 50 do
Trace.with_span ~__FILE__ ~__LINE__ "outer.loop" @@ fun _sp ->
for _j = 2 to 5 do
incr n;
Trace.with_span ~__FILE__ ~__LINE__ "inner.loop" @@ fun _sp ->
Trace.messagef (fun k -> k "hello %d %d" _i _j);
Trace.message "world";
Trace.counter_int "n" !n
done
done
let () =
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 in https://ui.perfetto.dev we get something like this:
On OCaml >= 4.12, and with ppxlib
installed, you can install ppx_trace
. This is a preprocessor that will rewrite like so:
let%trace f x y z =
do_sth x;
do_sth y;
begin
let%trace () = "sub-span" in
do_sth z
end
This more or less corresponds to:
let f x y z =
let _trace_span = Trace_core.enter_span ~__FILE__ ~__LINE__ "Foo.f" in
match
do_sth x;
do_sth y;
begin
let _trace_span = Trace_core.enter_span ~__FILE__ ~__LINE__ "sub-span" in
match do_sth z with
| res ->
Trace_core.exit_span _trace_span;
res
| exception e ->
Trace_core.exit_span _trace_span
raise e
end;
with
| res ->
Trace_core.exit_span _trace_span
res
| exception e ->
Trace_core.exit_span _trace_span
raise e
Alternatively, a name can be provided for the span, which is useful if you want to access it and use functions like Trace.add_data_to_span
:
let%trace f x y z =
do_sth x;
do_sth y;
begin
let%trace _sp = "sub-span" in
do_sth z;
Trace.add_data_to_span _sp ["x", `Int 42]
end
In your library
or executable
stanza, add: (preprocess (pps ppx_trace))
. The dependency on trace.core
is automatically added. You still need to configure a backend to actually do collection.
Concrete tracing or observability formats such as:
Catapult
trace-tef
. (Can be opened in https://ui.perfetto.dev).jsonl
filetldrs
program to be in path.tracy-client.trace
)opentelemetry.trace
)The library trace.subscriber
defines composable subscribers, which are sets of callbacks that consume tracing events. Multiple subscribers can be aggregated together (with events being dispatched to all of them) and be installed as a normal collector.