mirror of
https://github.com/ocaml-tracing/ocaml-trace.git
synced 2026-03-09 12:23:32 -04:00
remove manual spans
there isn't a clear way to use them. The `tracing` library in Rust doesn't have an equivalent of this, instead futures re-enter and re-exit regular spans whenever they are polled. For us in OCaml the equivalent is to create a regular span whenever we schedule a future/fiber, and exit it when we yield.
This commit is contained in:
parent
d3e710605e
commit
cb468a0440
7 changed files with 876 additions and 1295 deletions
|
|
@ -48,37 +48,10 @@ module type S = sig
|
|||
other calls to enter/exit_span and {!with_span}.
|
||||
@since 0.6 *)
|
||||
|
||||
val enter_manual_span :
|
||||
parent:explicit_span option ->
|
||||
flavor:[ `Sync | `Async ] option ->
|
||||
__FUNCTION__:string option ->
|
||||
__FILE__:string ->
|
||||
__LINE__:int ->
|
||||
data:(string * user_data) list ->
|
||||
string ->
|
||||
explicit_span
|
||||
(** Enter an explicit span. Surrounding scope, if any, is provided by [parent],
|
||||
and this function can store as much metadata as it wants in the hmap
|
||||
in the {!explicit_span}'s [meta] field.
|
||||
|
||||
This means that the collector doesn't need to implement contextual
|
||||
storage mapping {!span} to scopes, metadata, etc. on its side;
|
||||
everything can be transmitted in the {!explicit_span}.
|
||||
@since 0.3 *)
|
||||
|
||||
val exit_manual_span : explicit_span -> unit
|
||||
(** Exit an explicit span.
|
||||
@since 0.3 *)
|
||||
|
||||
val add_data_to_span : span -> (string * user_data) list -> unit
|
||||
(** @since Adds data to the current span.
|
||||
0.4 *)
|
||||
|
||||
val add_data_to_manual_span :
|
||||
explicit_span -> (string * user_data) list -> unit
|
||||
(** Adds data to the given span.
|
||||
@since 0.4 *)
|
||||
|
||||
val message : ?span:span -> data:(string * user_data) list -> string -> unit
|
||||
(** Emit a message with associated metadata. *)
|
||||
|
||||
|
|
|
|||
|
|
@ -42,34 +42,6 @@ let[@inline] exit_span sp : unit =
|
|||
| None -> ()
|
||||
| Some (module C) -> C.exit_span sp
|
||||
|
||||
let enter_explicit_span_collector_ (module C : Collector.S) ~parent ~flavor
|
||||
?__FUNCTION__ ~__FILE__ ~__LINE__ ?(data = data_empty_build_) name :
|
||||
explicit_span =
|
||||
let data = data () in
|
||||
C.enter_manual_span ~parent ~flavor ~__FUNCTION__ ~__FILE__ ~__LINE__ ~data
|
||||
name
|
||||
|
||||
let[@inline] enter_manual_sub_span ~parent ?flavor ?__FUNCTION__ ~__FILE__
|
||||
~__LINE__ ?data name : explicit_span =
|
||||
match A.get collector with
|
||||
| None -> Collector.dummy_explicit_span
|
||||
| Some coll ->
|
||||
enter_explicit_span_collector_ coll ~parent:(Some parent) ~flavor
|
||||
?__FUNCTION__ ~__FILE__ ~__LINE__ ?data name
|
||||
|
||||
let[@inline] enter_manual_toplevel_span ?flavor ?__FUNCTION__ ~__FILE__
|
||||
~__LINE__ ?data name : explicit_span =
|
||||
match A.get collector with
|
||||
| None -> Collector.dummy_explicit_span
|
||||
| Some coll ->
|
||||
enter_explicit_span_collector_ coll ~parent:None ~flavor ?__FUNCTION__
|
||||
~__FILE__ ~__LINE__ ?data name
|
||||
|
||||
let[@inline] exit_manual_span espan : unit =
|
||||
match A.get collector with
|
||||
| None -> ()
|
||||
| Some (module C) -> C.exit_manual_span espan
|
||||
|
||||
let[@inline] add_data_to_span sp data : unit =
|
||||
if data <> [] then (
|
||||
match A.get collector with
|
||||
|
|
@ -77,13 +49,6 @@ let[@inline] add_data_to_span sp data : unit =
|
|||
| Some (module C) -> C.add_data_to_span sp data
|
||||
)
|
||||
|
||||
let[@inline] add_data_to_manual_span esp data : unit =
|
||||
if data <> [] then (
|
||||
match A.get collector with
|
||||
| None -> ()
|
||||
| Some (module C) -> C.add_data_to_manual_span esp data
|
||||
)
|
||||
|
||||
let message_collector_ (module C : Collector.S) ?span
|
||||
?(data = data_empty_build_) msg : unit =
|
||||
let data = data () in
|
||||
|
|
|
|||
|
|
@ -30,8 +30,7 @@ val with_span :
|
|||
{b NOTE} an important restriction is that this is only supposed to
|
||||
work for synchronous, direct style code. Monadic concurrency, Effect-based
|
||||
fibers, etc. might not play well with this style of spans on some
|
||||
or all backends. If you use cooperative concurrency,
|
||||
see {!enter_manual_span}.
|
||||
or all backends.
|
||||
*)
|
||||
|
||||
val enter_span :
|
||||
|
|
@ -49,52 +48,6 @@ val add_data_to_span : span -> (string * user_data) list -> unit
|
|||
Behavior is not specified if the span has been exited.
|
||||
@since 0.4 *)
|
||||
|
||||
val enter_manual_sub_span :
|
||||
parent:explicit_span ->
|
||||
?flavor:[ `Sync | `Async ] ->
|
||||
?__FUNCTION__:string ->
|
||||
__FILE__:string ->
|
||||
__LINE__:int ->
|
||||
?data:(unit -> (string * user_data) list) ->
|
||||
string ->
|
||||
explicit_span
|
||||
(** Like {!with_span} but the caller is responsible for
|
||||
obtaining the [parent] span from their {e own} caller, and carry the resulting
|
||||
{!explicit_span} to the matching {!exit_manual_span}.
|
||||
@param flavor a description of the span that can be used by the {!Collector.S}
|
||||
to decide how to represent the span. Typically, [`Sync] spans
|
||||
start and stop on one thread, and are nested purely by their timestamp;
|
||||
and [`Async] spans can overlap, migrate between threads, etc. (as happens in
|
||||
Lwt, Eio, Async, etc.) which impacts how the collector might represent them.
|
||||
@since 0.3 *)
|
||||
|
||||
val enter_manual_toplevel_span :
|
||||
?flavor:[ `Sync | `Async ] ->
|
||||
?__FUNCTION__:string ->
|
||||
__FILE__:string ->
|
||||
__LINE__:int ->
|
||||
?data:(unit -> (string * user_data) list) ->
|
||||
string ->
|
||||
explicit_span
|
||||
(** Like {!with_span} but the caller is responsible for carrying this
|
||||
[explicit_span] around until it's exited with {!exit_manual_span}.
|
||||
The span can be used as a parent in {!enter_manual_sub_span}.
|
||||
@param flavor see {!enter_manual_sub_span} for more details.
|
||||
@since 0.3 *)
|
||||
|
||||
val exit_manual_span : explicit_span -> unit
|
||||
(** Exit an explicit span. This can be on another thread, in a
|
||||
fiber or lightweight thread, etc. and will be supported by backends
|
||||
nonetheless.
|
||||
The span can be obtained via {!enter_manual_sub_span} or
|
||||
{!enter_manual_toplevel_span}.
|
||||
@since 0.3 *)
|
||||
|
||||
val add_data_to_manual_span : explicit_span -> (string * user_data) list -> unit
|
||||
(** [add_data_explicit esp data] adds [data] to the span [esp].
|
||||
The behavior is not specified is the span has been exited already.
|
||||
@since 0.4 *)
|
||||
|
||||
val message :
|
||||
?span:span -> ?data:(unit -> (string * user_data) list) -> string -> unit
|
||||
(** [message msg] logs a message [msg] (if a collector is installed).
|
||||
|
|
|
|||
|
|
@ -103,15 +103,6 @@ end = struct
|
|||
with Found i -> Some i
|
||||
end
|
||||
|
||||
type async_span_info = {
|
||||
async_id: int;
|
||||
flavor: [ `Sync | `Async ] option;
|
||||
name: string;
|
||||
mutable data: (string * user_data) list;
|
||||
}
|
||||
|
||||
let key_async_data : async_span_info Meta_map.Key.t = Meta_map.Key.create ()
|
||||
|
||||
open struct
|
||||
let state_id_ = A.make 0
|
||||
|
||||
|
|
@ -131,7 +122,6 @@ type per_thread_state = {
|
|||
type state = {
|
||||
active: bool A.t;
|
||||
events: Bg_thread.event B_queue.t;
|
||||
span_id_gen: int A.t; (** Used for async spans *)
|
||||
bg_thread: Thread.t;
|
||||
buf_pool: Buf_pool.t;
|
||||
next_thread_ref: int A.t; (** in [0x01 .. 0xff], to allocate thread refs *)
|
||||
|
|
@ -287,41 +277,6 @@ struct
|
|||
| None -> !on_tracing_error (spf "unknown span %Ld" span)
|
||||
| Some idx -> Span_info_stack.add_data tls.spans idx data
|
||||
|
||||
let enter_manual_span ~(parent : explicit_span option) ~flavor ~__FUNCTION__:_
|
||||
~__FILE__:_ ~__LINE__:_ ~data name : explicit_span =
|
||||
let out, tls = get_thread_output () in
|
||||
let time_ns = Time.now_ns () in
|
||||
|
||||
(* get the id, or make a new one *)
|
||||
let async_id =
|
||||
match parent with
|
||||
| Some m -> (Meta_map.find_exn key_async_data m.meta).async_id
|
||||
| None -> A.fetch_and_add st.span_id_gen 1
|
||||
in
|
||||
|
||||
FWrite.Event.Async_begin.encode out ~name ~args:data ~t_ref:tls.thread_ref
|
||||
~time_ns ~async_id ();
|
||||
{
|
||||
span = 0L;
|
||||
meta =
|
||||
Meta_map.(
|
||||
empty |> add key_async_data { async_id; name; flavor; data = [] });
|
||||
}
|
||||
|
||||
let exit_manual_span (es : explicit_span) : unit =
|
||||
let { async_id; name; data; flavor = _ } =
|
||||
Meta_map.find_exn key_async_data es.meta
|
||||
in
|
||||
let out, tls = get_thread_output () in
|
||||
let time_ns = Time.now_ns () in
|
||||
|
||||
FWrite.Event.Async_end.encode out ~name ~t_ref:tls.thread_ref ~time_ns
|
||||
~args:data ~async_id ()
|
||||
|
||||
let add_data_to_manual_span (es : explicit_span) data =
|
||||
let m = Meta_map.find_exn key_async_data es.meta in
|
||||
m.data <- List.rev_append data m.data
|
||||
|
||||
let message ?span:_ ~data msg : unit =
|
||||
let out, tls = get_thread_output () in
|
||||
let time_ns = Time.now_ns () in
|
||||
|
|
@ -368,7 +323,6 @@ let create ~out () : collector =
|
|||
buf_pool;
|
||||
bg_thread;
|
||||
events;
|
||||
span_id_gen = A.make 0;
|
||||
next_thread_ref = A.make 1;
|
||||
per_thread = Array.init 16 (fun _ -> A.make Int_map.empty);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,23 +49,6 @@ type event =
|
|||
id: span;
|
||||
data: (string * user_data) list;
|
||||
}
|
||||
| E_enter_manual_span of {
|
||||
tid: int;
|
||||
name: string;
|
||||
time_us: float;
|
||||
id: int;
|
||||
flavor: [ `Sync | `Async ] option;
|
||||
fun_name: string option;
|
||||
data: (string * user_data) list;
|
||||
}
|
||||
| E_exit_manual_span of {
|
||||
tid: int;
|
||||
name: string;
|
||||
time_us: float;
|
||||
flavor: [ `Sync | `Async ] option;
|
||||
data: (string * user_data) list;
|
||||
id: int;
|
||||
}
|
||||
| E_counter of {
|
||||
name: string;
|
||||
tid: int;
|
||||
|
|
@ -91,15 +74,6 @@ type span_info = {
|
|||
mutable data: (string * user_data) list;
|
||||
}
|
||||
|
||||
(** key used to carry a unique "id" for all spans in an async context *)
|
||||
let key_async_id : int Meta_map.Key.t = Meta_map.Key.create ()
|
||||
|
||||
let key_async_data : (string * [ `Sync | `Async ] option) Meta_map.Key.t =
|
||||
Meta_map.Key.create ()
|
||||
|
||||
let key_data : (string * user_data) list ref Meta_map.Key.t =
|
||||
Meta_map.Key.create ()
|
||||
|
||||
(** Writer: knows how to write entries to a file in TEF format *)
|
||||
module Writer = struct
|
||||
type t = {
|
||||
|
|
@ -200,30 +174,6 @@ module Writer = struct
|
|||
args;
|
||||
Buffer.output_buffer self.oc self.buf
|
||||
|
||||
let emit_manual_begin ~tid ~name ~id ~ts ~args ~flavor (self : t) : unit =
|
||||
emit_sep_and_start_ self;
|
||||
Printf.bprintf self.buf
|
||||
{json|{"pid":%d,"cat":"trace","id":%d,"tid": %d,"ts": %.2f,"name":%a,"ph":"%c"%a}|json}
|
||||
self.pid id tid ts str_val name
|
||||
(match flavor with
|
||||
| None | Some `Async -> 'b'
|
||||
| Some `Sync -> 'B')
|
||||
(emit_args_o_ pp_user_data_)
|
||||
args;
|
||||
Buffer.output_buffer self.oc self.buf
|
||||
|
||||
let emit_manual_end ~tid ~name ~id ~ts ~flavor ~args (self : t) : unit =
|
||||
emit_sep_and_start_ self;
|
||||
Printf.bprintf self.buf
|
||||
{json|{"pid":%d,"cat":"trace","id":%d,"tid": %d,"ts": %.2f,"name":%a,"ph":"%c"%a}|json}
|
||||
self.pid id tid ts str_val name
|
||||
(match flavor with
|
||||
| None | Some `Async -> 'e'
|
||||
| Some `Sync -> 'E')
|
||||
(emit_args_o_ pp_user_data_)
|
||||
args;
|
||||
Buffer.output_buffer self.oc self.buf
|
||||
|
||||
let emit_instant_event ~tid ~name ~ts ~args (self : t) : unit =
|
||||
emit_sep_and_start_ self;
|
||||
Printf.bprintf self.buf
|
||||
|
|
@ -298,13 +248,6 @@ let bg_thread ~out (events : event B_queue.t) : unit =
|
|||
(match Span_tbl.find_opt spans id with
|
||||
| None -> !on_tracing_error (Printf.sprintf "cannot find span %Ld" id)
|
||||
| Some info -> info.data <- List.rev_append data info.data)
|
||||
| E_enter_manual_span { tid; time_us; name; id; data; fun_name; flavor } ->
|
||||
let data = add_fun_name_ fun_name data in
|
||||
Writer.emit_manual_begin ~tid ~name ~id ~ts:time_us ~args:data ~flavor
|
||||
writer
|
||||
| E_exit_manual_span { tid; time_us; name; id; flavor; data } ->
|
||||
Writer.emit_manual_end ~tid ~name ~id ~ts:time_us ~flavor ~args:data
|
||||
writer
|
||||
| E_counter { tid; name; time_us; n } ->
|
||||
Writer.emit_counter ~name ~tid ~ts:time_us writer n
|
||||
| E_name_process { name } -> Writer.emit_name_process ~name writer
|
||||
|
|
@ -409,48 +352,6 @@ let collector ~out () : collector =
|
|||
let add_data_to_span span data =
|
||||
if data <> [] then B_queue.push events (E_add_data { id = span; data })
|
||||
|
||||
let enter_manual_span ~(parent : explicit_span option) ~flavor
|
||||
~__FUNCTION__:fun_name ~__FILE__:_ ~__LINE__:_ ~data name :
|
||||
explicit_span =
|
||||
(* get the id, or make a new one *)
|
||||
let id =
|
||||
match parent with
|
||||
| Some m -> Meta_map.find_exn key_async_id m.meta
|
||||
| None -> A.fetch_and_add span_id_gen_ 1
|
||||
in
|
||||
let time_us = now_us () in
|
||||
B_queue.push events
|
||||
(E_enter_manual_span
|
||||
{ id; time_us; tid = get_tid_ (); data; name; fun_name; flavor });
|
||||
{
|
||||
span = 0L;
|
||||
meta =
|
||||
Meta_map.(
|
||||
empty |> add key_async_id id |> add key_async_data (name, flavor));
|
||||
}
|
||||
|
||||
let exit_manual_span (es : explicit_span) : unit =
|
||||
let id = Meta_map.find_exn key_async_id es.meta in
|
||||
let name, flavor = Meta_map.find_exn key_async_data es.meta in
|
||||
let data =
|
||||
try !(Meta_map.find_exn key_data es.meta) with Not_found -> []
|
||||
in
|
||||
let time_us = now_us () in
|
||||
let tid = get_tid_ () in
|
||||
B_queue.push events
|
||||
(E_exit_manual_span { tid; id; name; time_us; data; flavor })
|
||||
|
||||
let add_data_to_manual_span (es : explicit_span) data =
|
||||
if data <> [] then (
|
||||
let data_ref, add =
|
||||
try Meta_map.find_exn key_data es.meta, false
|
||||
with Not_found -> ref [], true
|
||||
in
|
||||
let new_data = List.rev_append data !data_ref in
|
||||
data_ref := new_data;
|
||||
if add then es.meta <- Meta_map.add key_data data_ref es.meta
|
||||
)
|
||||
|
||||
let message ?span:_ ~data msg : unit =
|
||||
let time_us = now_us () in
|
||||
let tid = get_tid_ () in
|
||||
|
|
|
|||
1892
test/t1.expected
1892
test/t1.expected
File diff suppressed because it is too large
Load diff
23
test/t1.ml
23
test/t1.ml
|
|
@ -1,3 +1,5 @@
|
|||
let ( let@ ) = ( @@ )
|
||||
|
||||
let run () =
|
||||
Trace.set_process_name "main";
|
||||
Trace.set_thread_name "t1";
|
||||
|
|
@ -6,10 +8,6 @@ let run () =
|
|||
|
||||
for _i = 1 to 50 do
|
||||
Trace.with_span ~__FILE__ ~__LINE__ "outer.loop" @@ fun _sp ->
|
||||
let pseudo_async_sp =
|
||||
Trace.enter_manual_toplevel_span ~__FILE__ ~__LINE__ "fake_sleep"
|
||||
in
|
||||
|
||||
for _j = 2 to 5 do
|
||||
incr n;
|
||||
Trace.with_span ~__FILE__ ~__LINE__ "inner.loop" @@ fun _sp ->
|
||||
|
|
@ -21,23 +19,10 @@ let run () =
|
|||
|
||||
if _j = 2 then (
|
||||
Trace.add_data_to_span _sp [ "j", `Int _j ];
|
||||
let _sp =
|
||||
Trace.enter_manual_sub_span ~parent:pseudo_async_sp
|
||||
~flavor:
|
||||
(if _i mod 3 = 0 then
|
||||
`Sync
|
||||
else
|
||||
`Async)
|
||||
~__FILE__ ~__LINE__ "sub-sleep"
|
||||
in
|
||||
let@ _sp = Trace.with_span ~__LINE__ ~__FILE__ "sub-sleep" in
|
||||
|
||||
(* fake micro sleep *)
|
||||
Thread.delay 0.005;
|
||||
Trace.exit_manual_span _sp
|
||||
) else if _j = 3 then (
|
||||
(* pretend some task finished. Note that this is not well scoped wrt other spans. *)
|
||||
Trace.add_data_to_manual_span pseudo_async_sp [ "slept", `Bool true ];
|
||||
Trace.exit_manual_span pseudo_async_sp
|
||||
Thread.delay 0.005
|
||||
)
|
||||
done
|
||||
done
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue