diff --git a/src/core/key_value.ml b/src/core/key_value.ml index 6760c340..36ee087c 100644 --- a/src/core/key_value.ml +++ b/src/core/key_value.ml @@ -6,3 +6,6 @@ let conv (k, v) = let open Proto.Common in let value = Value.conv v in make_key_value ~key:k ?value () + +let of_otel (kv : Proto.Common.key_value) : t = + kv.key, Value.of_otel_opt kv.value diff --git a/src/core/opentelemetry.ml b/src/core/opentelemetry.ml index ec84d0e4..73e34600 100644 --- a/src/core/opentelemetry.ml +++ b/src/core/opentelemetry.ml @@ -1,7 +1,5 @@ (** Opentelemetry types and instrumentation *) -open Common_ - module Rand_bytes = Rand_bytes (** Generation of random identifiers. *) @@ -29,9 +27,6 @@ module Timestamp_ns = Timestamp_ns module Exporter = Exporter module Collector = Exporter [@@deprecated "Use 'Exporter' instead"] -module Tick_callbacks = Tick_callbacks -(** Helper to implement part of the exporter *) - (** {2 Identifiers} *) module Trace_id = Trace_id @@ -63,10 +58,6 @@ module Span_link = Span_link module Span_status = Span_status module Span_kind = Span_kind -(** {2 Scopes} *) - -module Scope = Scope - (** {2 Traces} *) module Span = Span diff --git a/src/core/scope.ml b/src/core/scope.ml deleted file mode 100644 index aa5cb19a..00000000 --- a/src/core/scope.ml +++ /dev/null @@ -1,131 +0,0 @@ -open Common_ - -type item_list = - | Nil - | Ev of Event.t * item_list - | Attr of Key_value.t * item_list - | Span_link of Span_link.t * item_list - | Span_status of Span_status.t * item_list - | Span_kind of Span_kind.t * item_list - -type t = { - trace_id: Trace_id.t; - span_id: Span_id.t; - mutable items: item_list; -} - -let attrs scope = - let rec loop acc = function - | Nil -> acc - | Attr (attr, l) -> loop (attr :: acc) l - | Ev (_, l) | Span_kind (_, l) | Span_link (_, l) | Span_status (_, l) -> - loop acc l - in - loop [] scope.items - -let events scope = - let rec loop acc = function - | Nil -> acc - | Ev (event, l) -> loop (event :: acc) l - | Attr (_, l) | Span_kind (_, l) | Span_link (_, l) | Span_status (_, l) -> - loop acc l - in - loop [] scope.items - -let links scope = - let rec loop acc = function - | Nil -> acc - | Span_link (span_link, l) -> loop (span_link :: acc) l - | Ev (_, l) | Span_kind (_, l) | Attr (_, l) | Span_status (_, l) -> - loop acc l - in - loop [] scope.items - -let status scope = - let rec loop = function - | Nil -> None - | Span_status (status, _) -> Some status - | Ev (_, l) | Attr (_, l) | Span_kind (_, l) | Span_link (_, l) -> loop l - in - loop scope.items - -let kind scope = - let rec loop = function - | Nil -> None - | Span_kind (k, _) -> Some k - | Ev (_, l) | Span_status (_, l) | Attr (_, l) | Span_link (_, l) -> loop l - in - loop scope.items - -let make ~trace_id ~span_id ?(events = []) ?(attrs = []) ?(links = []) ?status - () : t = - let items = - let items = - match status with - | None -> Nil - | Some status -> Span_status (status, Nil) - in - let items = List.fold_left (fun acc ev -> Ev (ev, acc)) items events in - let items = List.fold_left (fun acc attr -> Attr (attr, acc)) items attrs in - List.fold_left (fun acc link -> Span_link (link, acc)) items links - in - { trace_id; span_id; items } - -let[@inline] to_span_link ?trace_state ?attrs ?dropped_attributes_count - (self : t) : Span_link.t = - Span_link.make ?trace_state ?attrs ?dropped_attributes_count - ~trace_id:self.trace_id ~span_id:self.span_id () - -let[@inline] to_span_ctx (self : t) : Span_ctx.t = - Span_ctx.make ~trace_id:self.trace_id ~parent_id:self.span_id () - -open struct - let[@inline] is_not_dummy (self : t) : bool = Span_id.is_valid self.span_id -end - -let[@inline] add_event (self : t) (ev : unit -> Event.t) : unit = - if is_not_dummy self then self.items <- Ev (ev (), self.items) - -let[@inline] record_exception (self : t) (exn : exn) - (bt : Printexc.raw_backtrace) : unit = - if is_not_dummy self then ( - let ev = - Event.make "exception" - ~attrs: - [ - "exception.message", `String (Printexc.to_string exn); - "exception.type", `String (Printexc.exn_slot_name exn); - ( "exception.stacktrace", - `String (Printexc.raw_backtrace_to_string bt) ); - ] - in - self.items <- Ev (ev, self.items) - ) - -let[@inline] add_attrs (self : t) (attrs : unit -> Key_value.t list) : unit = - if is_not_dummy self then - self.items <- - List.fold_left (fun acc attr -> Attr (attr, acc)) self.items (attrs ()) - -let[@inline] add_links (self : t) (links : unit -> Span_link.t list) : unit = - if is_not_dummy self then - self.items <- - List.fold_left - (fun acc link -> Span_link (link, acc)) - self.items (links ()) - -let set_status (self : t) (status : Span_status.t) : unit = - if is_not_dummy self then self.items <- Span_status (status, self.items) - -let set_kind (self : t) (k : Span_kind.t) : unit = - if is_not_dummy self then self.items <- Span_kind (k, self.items) - -let ambient_scope_key : t Ambient_context.key = Ambient_context.create_key () - -let get_ambient_scope ?scope () : t option = - match scope with - | Some _ -> scope - | None -> Ambient_context.get ambient_scope_key - -let[@inline] with_ambient_scope (sc : t) (f : unit -> 'a) : 'a = - Ambient_context.with_binding ambient_scope_key sc (fun _ -> f ()) diff --git a/src/core/scope.mli b/src/core/scope.mli deleted file mode 100644 index 9ba60d0e..00000000 --- a/src/core/scope.mli +++ /dev/null @@ -1,89 +0,0 @@ -(** Scopes. - - A scope is a trace ID and the span ID of the currently active span. *) - -open Common_ - -type item_list - -type t = { - trace_id: Trace_id.t; - span_id: Span_id.t; - mutable items: item_list; -} - -val attrs : t -> Key_value.t list - -val events : t -> Event.t list - -val links : t -> Span_link.t list - -val status : t -> Span_status.t option - -val kind : t -> Span_kind.t option - -val make : - trace_id:Trace_id.t -> - span_id:Span_id.t -> - ?events:Event.t list -> - ?attrs:Key_value.t list -> - ?links:Span_link.t list -> - ?status:Span_status.t -> - unit -> - t - -val to_span_link : - ?trace_state:string -> - ?attrs:Key_value.t list -> - ?dropped_attributes_count:int -> - t -> - Span_link.t -(** Turn the scope into a span link *) - -val to_span_ctx : t -> Span_ctx.t -(** Turn the scope into a span context *) - -val add_event : t -> (unit -> Event.t) -> unit -(** Add an event to the scope. It will be aggregated into the span. - - Note that this takes a function that produces an event, and will only call - it if there is an instrumentation backend. *) - -val record_exception : t -> exn -> Printexc.raw_backtrace -> unit - -val add_attrs : t -> (unit -> Key_value.t list) -> unit -(** Add attributes to the scope. It will be aggregated into the span. - - Note that this takes a function that produces attributes, and will only call - it if there is an instrumentation backend. *) - -val add_links : t -> (unit -> Span_link.t list) -> unit -(** Add links to the scope. It will be aggregated into the span. - - Note that this takes a function that produces links, and will only call it - if there is an instrumentation backend. *) - -val set_status : t -> Span_status.t -> unit -(** set the span status. - - Note that this function will be called only if there is an instrumentation - backend. *) - -val set_kind : t -> Span_kind.t -> unit -(** Set the span's kind. - @since 0.11 *) - -val ambient_scope_key : t Ambient_context.key -(** The opaque key necessary to access/set the ambient scope with - {!Ambient_context}. *) - -val get_ambient_scope : ?scope:t -> unit -> t option -(** Obtain current scope from {!Ambient_context}, if available. *) - -val with_ambient_scope : t -> (unit -> 'a) -> 'a -(** [with_ambient_scope sc thunk] calls [thunk()] in a context where [sc] is the - (thread|continuation)-local scope, then reverts to the previous local scope, - if any. - - @see - ambient-context docs *) diff --git a/src/core/span.ml b/src/core/span.ml index 1ea8cb0b..f0135b40 100644 --- a/src/core/span.ml +++ b/src/core/span.ml @@ -22,11 +22,15 @@ type key_value = | `None ] -let id self = Span_id.of_bytes self.span_id +let[@inline] id self = Span_id.of_bytes self.span_id -let create ?(kind = !Globals.default_span_kind) ?(id = Span_id.create ()) - ?trace_state ?(attrs = []) ?(events = []) ?status ~trace_id ?parent - ?(links = []) ~start_time ~end_time name : t * id = +let[@inline] trace_id self = Trace_id.of_bytes self.trace_id + +let[@inline] is_not_dummy self = Span_id.is_valid (id self) + +let make ?(kind = !Globals.default_span_kind) ?trace_state ?(attrs = []) + ?(events = []) ?status ~trace_id ~id ?parent ?(links = []) ~start_time + ~end_time name : t = let trace_id = Trace_id.to_bytes trace_id in let parent_span_id = Option.map Span_id.to_bytes parent in let attributes = List.map Key_value.conv attrs in @@ -35,4 +39,85 @@ let create ?(kind = !Globals.default_span_kind) ?(id = Span_id.create ()) ~attributes ~events ?trace_state ?status ~kind ~name ~links ~start_time_unix_nano:start_time ~end_time_unix_nano:end_time () in - span, id + span + +let create_new ?kind ?(id = Span_id.create ()) ?trace_state ?attrs ?events + ?status ~trace_id ?parent ?links ~start_time ~end_time name : t = + make ?kind ~id ~trace_id ?trace_state ?attrs ?events ?status ?parent ?links + ~start_time ~end_time name + +let attrs self = self.attributes |> List.rev_map Key_value.of_otel + +let events self = self.events + +let links self : Span_link.t list = self.links + +let status self = self.status + +let kind self = + let k = self.kind in + if k = Span_kind_unspecified then + None + else + Some k + +let to_span_link (self : t) : Span_link.t = + make_span_link ~attributes:self.attributes + ?dropped_attributes_count: + (if span_has_dropped_attributes_count self then + Some self.dropped_attributes_count + else + None) + ?trace_state: + (if span_has_trace_state self then + Some self.trace_state + else + None) + ~trace_id:self.trace_id ~span_id:self.span_id () + +let[@inline] to_span_ctx (self : t) : Span_ctx.t = + Span_ctx.make ~trace_id:(trace_id self) ~parent_id:(id self) () + +let[@inline] add_event self ev : unit = + if is_not_dummy self then span_set_events self (ev :: self.events) + +let add_event' self ev : unit = if is_not_dummy self then add_event self (ev ()) + +let record_exception (self : t) (exn : exn) (bt : Printexc.raw_backtrace) : unit + = + if is_not_dummy self then ( + let ev = + Event.make "exception" + ~attrs: + [ + "exception.message", `String (Printexc.to_string exn); + "exception.type", `String (Printexc.exn_slot_name exn); + ( "exception.stacktrace", + `String (Printexc.raw_backtrace_to_string bt) ); + ] + in + add_event self ev + ) + +let[@inline] add_attrs (self : t) (attrs : unit -> Key_value.t list) : unit = + if is_not_dummy self then ( + let attrs = List.rev_map Key_value.conv (attrs ()) in + let attrs = List.rev_append attrs self.attributes in + span_set_attributes self attrs + ) + +let add_links (self : t) (links : Span_link.t list) : unit = + if links <> [] then ( + let links = List.rev_append links self.links in + span_set_links self links + ) + +let add_links' (self : t) (links : unit -> Span_link.t list) : unit = + if is_not_dummy self then ( + let links = List.rev_append (links ()) self.links in + span_set_links self links + ) + +let set_status = span_set_status + +let set_kind = span_set_kind diff --git a/src/core/span.mli b/src/core/span.mli index cfb9a2de..673654a9 100644 --- a/src/core/span.mli +++ b/src/core/span.mli @@ -24,9 +24,30 @@ val id : t -> Span_id.t type key_value = Key_value.t -val create : +val make : ?kind:kind -> - ?id:id -> + ?trace_state:string -> + ?attrs:key_value list -> + ?events:Event.t list -> + ?status:status -> + trace_id:Trace_id.t -> + id:Span_id.t -> + ?parent:id -> + ?links:Span_link.t list -> + start_time:Timestamp_ns.t -> + end_time:Timestamp_ns.t -> + string -> + t +(** [make ~trace_id ~id name] creates a new span + @param trace_id the trace this belongs to + @param parent parent span, if any + @param links + list of links to other spans, each with their trace state (see + {{:https://www.w3.org/TR/trace-context/#tracestate-header} w3.org}) *) + +val create_new : + ?kind:kind -> + ?id:Span_id.t -> ?trace_state:string -> ?attrs:key_value list -> ?events:Event.t list -> @@ -37,10 +58,50 @@ val create : start_time:Timestamp_ns.t -> end_time:Timestamp_ns.t -> string -> - t * id -(** [create ~trace_id name] creates a new span with its unique ID. - @param trace_id the trace this belongs to - @param parent parent span, if any - @param links - list of links to other spans, each with their trace state (see - {{:https://www.w3.org/TR/trace-context/#tracestate-header} w3.org}) *) + t + +val attrs : t -> Key_value.t list + +val events : t -> Event.t list + +val links : t -> Span_link.t list + +val status : t -> Span_status.t option + +val kind : t -> Span_kind.t option + +val to_span_link : t -> Span_link.t +(** Turn the scope into a span link *) + +val to_span_ctx : t -> Span_ctx.t +(** Turn the scope into a span context *) + +val add_event : t -> Event.t -> unit + +val add_event' : t -> (unit -> Event.t) -> unit +(** Add an event to the scope. It will be aggregated into the span. + + Note that this takes a function that produces an event, and will only call + it if there is an instrumentation backend. *) + +val record_exception : t -> exn -> Printexc.raw_backtrace -> unit + +val add_links : t -> Span_link.t list -> unit + +val add_links' : t -> (unit -> Span_link.t list) -> unit +(** Add links to the scope. It will be aggregated into the span. + + Note that this takes a function that produces links, and will only call it + if there is an instrumentation backend. *) + +val add_attrs : t -> (unit -> Key_value.t list) -> unit + +val set_status : t -> Span_status.t -> unit +(** set the span status. + + Note that this function will be called only if there is an instrumentation + backend. *) + +val set_kind : t -> Span_kind.t -> unit +(** Set the span's kind. + @since 0.11 *) diff --git a/src/core/tracer.ml b/src/core/tracer.ml index 6045df9d..f15c6985 100644 --- a/src/core/tracer.ml +++ b/src/core/tracer.ml @@ -49,11 +49,11 @@ let simple_main_exporter : t = {b NOTE} be careful not to call this inside a Gc alarm, as it can cause deadlocks. *) -let emit ?service_name:_ ?attrs:_ (spans : span list) : unit = +let (emit [@deprecated "use an explicit tracer"]) = + fun ?service_name:_ ?attrs:_ (spans : span list) : unit -> match Exporter.Main_exporter.get () with | None -> () | Some exp -> exp#send_trace spans -[@@deprecated "use an explicit tracer"] (* TODO: remove scope, use span directly *) type scope = Scope.t = { @@ -113,11 +113,11 @@ let with_' ?(tracer = simple_main_exporter) ?(force_new_trace_id = false) (make_status ~code:Status_code_error ~message:(Printexc.to_string e) ())) in - let span, _ = + let span = (* TODO: should the attrs passed to with_ go on the Span (in Span.create) or on the ResourceSpan (in emit)? (question also applies to Opentelemetry_lwt.Trace.with) *) - Span.create ?kind ~trace_id ?parent ~links:(Scope.links scope) ~id:span_id + Span.make ?kind ~trace_id ?parent ~links:(Scope.links scope) ~id:span_id ?trace_state ~attrs:(Scope.attrs scope) ~events:(Scope.events scope) ~start_time ~end_time:(Timestamp_ns.now_unix_ns ()) diff --git a/src/core/value.ml b/src/core/value.ml index 97fc0503..d3d07c0e 100644 --- a/src/core/value.ml +++ b/src/core/value.ml @@ -17,3 +17,11 @@ let conv = | `Bool b -> Some (Bool_value b) | `Float f -> Some (Double_value f) | `None -> None + +let of_otel_opt (v : Proto.Common.any_value option) : t = + match v with + | Some (Int_value i) -> `Int (Int64.to_int i) + | Some (String_value s) -> `String s + | Some (Bool_value b) -> `Bool b + | Some (Double_value f) -> `Float f + | Some (Array_value _ | Kvlist_value _ | Bytes_value _) | None -> `None