feat: add levels to Trace_core.

these levels are used to control the verbosity levels.
This commit is contained in:
Simon Cruanes 2024-02-25 22:16:23 -05:00
parent d0e159785e
commit de8b51a9a2
3 changed files with 145 additions and 40 deletions

34
src/core/level.ml Normal file
View file

@ -0,0 +1,34 @@
(** Tracing levels.
This is similar to log levels in, say, [Logs].
In a thoroughly instrumented program, there will be a {b lot}
of spans, and enabling them all in production might slow
down the application or overwhelm the tracing system; yet
they might be useful in debug situations.
@since NEXT_RELEASE *)
(** Level of tracing. These levels are in increasing order, i.e if
level [Debug1] is enabled, everything below it (Error, Warning, Info, etc.)
are also enabled.
@since NEXT_RELEASE *)
type t =
| Error (** Only errors *)
| Warning (** Warnings *)
| Info
| Debug1 (** Least verbose debugging level *)
| Debug2 (** Intermediate verbosity debugging level *)
| Debug3 (** Maximum verbosity debugging level *)
| Trace (** Enable everything (default level) *)
(** @since NEXT_RELEASE *)
let to_string : t -> string = function
| Error -> "error"
| Warning -> "warning"
| Info -> "info"
| Debug1 -> "debug1"
| Debug2 -> "debug2"
| Debug3 -> "debug3"
| Trace -> "trace"
let[@inline] leq (a : t) (b : t) : bool = a <= b

View file

@ -2,12 +2,21 @@ include Types
module A = Atomic_ module A = Atomic_
module Collector = Collector module Collector = Collector
module Meta_map = Meta_map module Meta_map = Meta_map
module Level = Level
type collector = (module Collector.S) type collector = (module Collector.S)
(* ## globals ## *)
(** Global collector. *) (** Global collector. *)
let collector : collector option A.t = A.make None let collector : collector option A.t = A.make None
(* default level for spans without a level *)
let default_level_ = A.make Level.Trace
let current_level_ = A.make Level.Trace
(* ## implementation ## *)
let data_empty_build_ () = [] let data_empty_build_ () = []
let[@inline] enabled () = let[@inline] enabled () =
@ -15,27 +24,34 @@ let[@inline] enabled () =
| None -> false | None -> false
| Some _ -> true | Some _ -> true
let[@inline] set_default_level l = A.set default_level_ l
let[@inline] set_current_level l = A.set current_level_ l
let[@inline] get_current_level () = A.get current_level_
let[@inline] check_level ?(level = A.get default_level_) () : bool =
Level.leq level (A.get current_level_)
let with_span_collector_ (module C : Collector.S) ?__FUNCTION__ ~__FILE__ let with_span_collector_ (module C : Collector.S) ?__FUNCTION__ ~__FILE__
~__LINE__ ?(data = data_empty_build_) name f = ~__LINE__ ?(data = data_empty_build_) name f =
let data = data () in let data = data () in
C.with_span ~__FUNCTION__ ~__FILE__ ~__LINE__ ~data name f C.with_span ~__FUNCTION__ ~__FILE__ ~__LINE__ ~data name f
let[@inline] with_span ?__FUNCTION__ ~__FILE__ ~__LINE__ ?data name f = let[@inline] with_span ?level ?__FUNCTION__ ~__FILE__ ~__LINE__ ?data name f =
match A.get collector with match A.get collector with
| None -> | Some collector when check_level ?level () ->
(* fast path: no collector, no span *)
f Collector.dummy_span
| Some collector ->
with_span_collector_ collector ?__FUNCTION__ ~__FILE__ ~__LINE__ ?data name with_span_collector_ collector ?__FUNCTION__ ~__FILE__ ~__LINE__ ?data name
f f
| _ ->
(* fast path: no collector, no span *)
f Collector.dummy_span
let[@inline] enter_span ?__FUNCTION__ ~__FILE__ ~__LINE__ let[@inline] enter_span ?level ?__FUNCTION__ ~__FILE__ ~__LINE__
?(data = data_empty_build_) name : span = ?(data = data_empty_build_) name : span =
match A.get collector with match A.get collector with
| None -> Collector.dummy_span | Some (module C) when check_level ?level () ->
| Some (module C) ->
let data = data () in let data = data () in
C.enter_span ~__FUNCTION__ ~__FILE__ ~__LINE__ ~data name C.enter_span ~__FUNCTION__ ~__FILE__ ~__LINE__ ~data name
| _ -> Collector.dummy_span
let[@inline] exit_span sp : unit = let[@inline] exit_span sp : unit =
match A.get collector with match A.get collector with
@ -49,21 +65,21 @@ let enter_explicit_span_collector_ (module C : Collector.S) ~parent ~flavor
C.enter_manual_span ~parent ~flavor ~__FUNCTION__ ~__FILE__ ~__LINE__ ~data C.enter_manual_span ~parent ~flavor ~__FUNCTION__ ~__FILE__ ~__LINE__ ~data
name name
let[@inline] enter_manual_sub_span ~parent ?flavor ?__FUNCTION__ ~__FILE__ let[@inline] enter_manual_sub_span ~parent ?flavor ?level ?__FUNCTION__
~__LINE__ ?data name : explicit_span = ~__FILE__ ~__LINE__ ?data name : explicit_span =
match A.get collector with match A.get collector with
| None -> Collector.dummy_explicit_span | Some coll when check_level ?level () ->
| Some coll ->
enter_explicit_span_collector_ coll ~parent:(Some parent) ~flavor enter_explicit_span_collector_ coll ~parent:(Some parent) ~flavor
?__FUNCTION__ ~__FILE__ ~__LINE__ ?data name ?__FUNCTION__ ~__FILE__ ~__LINE__ ?data name
| _ -> Collector.dummy_explicit_span
let[@inline] enter_manual_toplevel_span ?flavor ?__FUNCTION__ ~__FILE__ let[@inline] enter_manual_toplevel_span ?flavor ?level ?__FUNCTION__ ~__FILE__
~__LINE__ ?data name : explicit_span = ~__LINE__ ?data name : explicit_span =
match A.get collector with match A.get collector with
| None -> Collector.dummy_explicit_span | Some coll when check_level ?level () ->
| Some coll ->
enter_explicit_span_collector_ coll ~parent:None ~flavor ?__FUNCTION__ enter_explicit_span_collector_ coll ~parent:None ~flavor ?__FUNCTION__
~__FILE__ ~__LINE__ ?data name ~__FILE__ ~__LINE__ ?data name
| _ -> Collector.dummy_explicit_span
let[@inline] exit_manual_span espan : unit = let[@inline] exit_manual_span espan : unit =
match A.get collector with match A.get collector with
@ -89,15 +105,15 @@ let message_collector_ (module C : Collector.S) ?span
let data = data () in let data = data () in
C.message ?span ~data msg C.message ?span ~data msg
let[@inline] message ?span ?data msg : unit = let[@inline] message ?level ?span ?data msg : unit =
match A.get collector with match A.get collector with
| None -> () | Some coll when check_level ?level () ->
| Some coll -> message_collector_ coll ?span ?data msg message_collector_ coll ?span ?data msg
| _ -> ()
let messagef ?span ?data k = let messagef ?level ?span ?data k =
match A.get collector with match A.get collector with
| None -> () | Some (module C) when check_level ?level () ->
| Some (module C) ->
k (fun fmt -> k (fun fmt ->
Format.kasprintf Format.kasprintf
(fun str -> (fun str ->
@ -108,20 +124,21 @@ let messagef ?span ?data k =
in in
C.message ?span ~data str) C.message ?span ~data str)
fmt) fmt)
| _ -> ()
let counter_int ?(data = data_empty_build_) name n : unit = let counter_int ?level ?(data = data_empty_build_) name n : unit =
match A.get collector with match A.get collector with
| None -> () | Some (module C) when check_level ?level () ->
| Some (module C) ->
let data = data () in let data = data () in
C.counter_int ~data name n C.counter_int ~data name n
| _ -> ()
let counter_float ?(data = data_empty_build_) name f : unit = let counter_float ?level ?(data = data_empty_build_) name f : unit =
match A.get collector with match A.get collector with
| None -> () | Some (module C) when check_level ?level () ->
| Some (module C) ->
let data = data () in let data = data () in
C.counter_float ~data name f C.counter_float ~data name f
| _ -> ()
let set_thread_name name : unit = let set_thread_name name : unit =
match A.get collector with match A.get collector with

View file

@ -3,6 +3,16 @@
include module type of Types include module type of Types
module Collector = Collector module Collector = Collector
module Meta_map = Meta_map module Meta_map = Meta_map
module Level = Level
(**/**)
(* no guarantee of stability *)
module Internal_ : sig
module Atomic_ = Atomic_
end
(**/**)
(** {2 Tracing} *) (** {2 Tracing} *)
@ -12,7 +22,12 @@ val enabled : unit -> bool
This is fast, so that the traced program can check it before creating This is fast, so that the traced program can check it before creating
any span or message. *) any span or message. *)
val set_default_level : Level.t -> unit
(** Set level used for spans that do not specify it. The default
default value is [Level.Trace] *)
val with_span : val with_span :
?level:Level.t ->
?__FUNCTION__:string -> ?__FUNCTION__:string ->
__FILE__:string -> __FILE__:string ->
__LINE__:int -> __LINE__:int ->
@ -27,6 +42,9 @@ val with_span :
This is the recommended way to instrument most code. This is the recommended way to instrument most code.
@param level optional level for this span. since NEXT_RELEASE.
Default is set via {!set_default_level}.
{b NOTE} an important restriction is that this is only supposed to {b NOTE} an important restriction is that this is only supposed to
work for synchronous, direct style code. Monadic concurrency, Effect-based work for synchronous, direct style code. Monadic concurrency, Effect-based
fibers, etc. might not play well with this style of spans on some fibers, etc. might not play well with this style of spans on some
@ -35,14 +53,22 @@ val with_span :
*) *)
val enter_span : val enter_span :
?level:Level.t ->
?__FUNCTION__:string -> ?__FUNCTION__:string ->
__FILE__:string -> __FILE__:string ->
__LINE__:int -> __LINE__:int ->
?data:(unit -> (string * user_data) list) -> ?data:(unit -> (string * user_data) list) ->
string -> string ->
span span
(** Enter a span manually.
@param level optional level for this span. since NEXT_RELEASE.
Default is set via {!set_default_level}. *)
val exit_span : span -> unit val exit_span : span -> unit
(** Exit a span manually. This must run on the same thread
as the corresponding {!enter_span}, and spans must nest
correctly. *)
val add_data_to_span : span -> (string * user_data) list -> unit val add_data_to_span : span -> (string * user_data) list -> unit
(** Add structured data to the given active span (see {!with_span}). (** Add structured data to the given active span (see {!with_span}).
@ -52,6 +78,7 @@ val add_data_to_span : span -> (string * user_data) list -> unit
val enter_manual_sub_span : val enter_manual_sub_span :
parent:explicit_span -> parent:explicit_span ->
?flavor:[ `Sync | `Async ] -> ?flavor:[ `Sync | `Async ] ->
?level:Level.t ->
?__FUNCTION__:string -> ?__FUNCTION__:string ->
__FILE__:string -> __FILE__:string ->
__LINE__:int -> __LINE__:int ->
@ -66,10 +93,13 @@ val enter_manual_sub_span :
start and stop on one thread, and are nested purely by their timestamp; 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 and [`Async] spans can overlap, migrate between threads, etc. (as happens in
Lwt, Eio, Async, etc.) which impacts how the collector might represent them. Lwt, Eio, Async, etc.) which impacts how the collector might represent them.
@param level optional level for this span. since NEXT_RELEASE.
Default is set via {!set_default_level}.
@since 0.3 *) @since 0.3 *)
val enter_manual_toplevel_span : val enter_manual_toplevel_span :
?flavor:[ `Sync | `Async ] -> ?flavor:[ `Sync | `Async ] ->
?level:Level.t ->
?__FUNCTION__:string -> ?__FUNCTION__:string ->
__FILE__:string -> __FILE__:string ->
__LINE__:int -> __LINE__:int ->
@ -80,6 +110,8 @@ val enter_manual_toplevel_span :
[explicit_span] around until it's exited with {!exit_manual_span}. [explicit_span] around until it's exited with {!exit_manual_span}.
The span can be used as a parent in {!enter_manual_sub_span}. The span can be used as a parent in {!enter_manual_sub_span}.
@param flavor see {!enter_manual_sub_span} for more details. @param flavor see {!enter_manual_sub_span} for more details.
@param level optional level for this span. since NEXT_RELEASE.
Default is set via {!set_default_level}.
@since 0.3 *) @since 0.3 *)
val exit_manual_span : explicit_span -> unit val exit_manual_span : explicit_span -> unit
@ -96,19 +128,28 @@ val add_data_to_manual_span : explicit_span -> (string * user_data) list -> unit
@since 0.4 *) @since 0.4 *)
val message : val message :
?span:span -> ?data:(unit -> (string * user_data) list) -> string -> unit ?level:Level.t ->
?span:span ->
?data:(unit -> (string * user_data) list) ->
string ->
unit
(** [message msg] logs a message [msg] (if a collector is installed). (** [message msg] logs a message [msg] (if a collector is installed).
Additional metadata can be provided. Additional metadata can be provided.
@param level optional level for this span. since NEXT_RELEASE.
Default is set via {!set_default_level}.
@param span the surrounding span, if any. This might be ignored by the collector. *) @param span the surrounding span, if any. This might be ignored by the collector. *)
val messagef : val messagef :
?level:Level.t ->
?span:span -> ?span:span ->
?data:(unit -> (string * user_data) list) -> ?data:(unit -> (string * user_data) list) ->
((('a, Format.formatter, unit, unit) format4 -> 'a) -> unit) -> ((('a, Format.formatter, unit, unit) format4 -> 'a) -> unit) ->
unit unit
(** [messagef (fun k->k"hello %s %d!" "world" 42)] is like (** [messagef (fun k->k"hello %s %d!" "world" 42)] is like
[message "hello world 42!"] but only computes the string formatting [message "hello world 42!"] but only computes the string formatting
if a collector is installed. *) if a collector is installed.
@param level optional level for this span. since NEXT_RELEASE.
Default is set via {!set_default_level}. *)
val set_thread_name : string -> unit val set_thread_name : string -> unit
(** Give a name to the current thread. (** Give a name to the current thread.
@ -121,14 +162,26 @@ val set_process_name : string -> unit
to display traces in a more informative way. *) to display traces in a more informative way. *)
val counter_int : val counter_int :
?data:(unit -> (string * user_data) list) -> string -> int -> unit ?level:Level.t ->
?data:(unit -> (string * user_data) list) ->
string ->
int ->
unit
(** Emit a counter of type [int]. Counters represent the evolution of some quantity (** Emit a counter of type [int]. Counters represent the evolution of some quantity
over time. over time.
@param level optional level for this span. since NEXT_RELEASE.
Default is set via {!set_default_level}.
@param data metadata for this metric (since 0.4) *) @param data metadata for this metric (since 0.4) *)
val counter_float : val counter_float :
?data:(unit -> (string * user_data) list) -> string -> float -> unit ?level:Level.t ->
?data:(unit -> (string * user_data) list) ->
string ->
float ->
unit
(** Emit a counter of type [float]. See {!counter_int} for more details. (** Emit a counter of type [float]. See {!counter_int} for more details.
@param level optional level for this span. since NEXT_RELEASE.
Default is set via {!set_default_level}.
@param data metadata for this metric (since 0.4) *) @param data metadata for this metric (since 0.4) *)
(** {2 Collector} *) (** {2 Collector} *)
@ -143,15 +196,16 @@ val setup_collector : collector -> unit
@raise Invalid_argument if there already is an established @raise Invalid_argument if there already is an established
collector. *) collector. *)
val get_current_level : unit -> Level.t
(** Get current level. This is only meaningful if
a collector was set up with {!setup_collector}.
@since NEXT_RELEASE *)
val set_current_level : Level.t -> unit
(** Set the current level of tracing. This only has a visible
effect if a collector was installed with {!setup_collector}.
@since NEXT_RELEASE *)
val shutdown : unit -> unit val shutdown : unit -> unit
(** [shutdown ()] shutdowns the current collector, if one was installed, (** [shutdown ()] shutdowns the current collector, if one was installed,
and waits for it to terminate before returning. *) and waits for it to terminate before returning. *)
(**/**)
(* no guarantee of stability *)
module Internal_ : sig
module Atomic_ = Atomic_
end
(**/**)