diff --git a/src/core/opentelemetry.ml b/src/core/opentelemetry.ml index b08516e3..50103d89 100644 --- a/src/core/opentelemetry.ml +++ b/src/core/opentelemetry.ml @@ -794,164 +794,6 @@ end = struct ?attrs () end -(** {2 Scopes} *) - -(** Scopes. - - A scope is a trace ID and the span ID of the currently active span. -*) -module Scope : sig - type item_list - - type t = { - trace_id: Trace_id.t; - span_id: Span_id.t; - mutable items: item_list; - } - - val attrs : t -> key_value list - - val events : t -> Event.t list - - val links : t -> Span_link.t list - - val make : - trace_id:Trace_id.t -> - span_id:Span_id.t -> - ?events:Event.t list -> - ?attrs:key_value list -> - ?links:Span_link.t list -> - unit -> - t - - val to_span_ctx : t -> Span_ctx.t - - val add_event : t -> (unit -> Event.t) -> unit - - val record_exception : t -> exn -> Printexc.raw_backtrace -> unit - - val add_attrs : t -> (unit -> key_value list) -> unit - - val add_links : t -> (unit -> Span_link.t list) -> unit - - val ambient_scope_key : t Ambient_context.key - - val get_ambient_scope : ?scope:t -> unit -> t option - - val with_ambient_scope : t -> (unit -> 'a) -> 'a -end = struct - type item_list = - | Nil - | Ev of Event.t * item_list - | Attr of key_value * item_list - | Span_link of Span_link.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_link (_, 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_link (_, 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) | Attr (_, l) -> loop acc l - in - loop [] scope.items - - let make ~trace_id ~span_id ?(events = []) ?(attrs = []) ?(links = []) () : t - = - let items = - let items = List.fold_left (fun acc ev -> Ev (ev, acc)) Nil 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 } - - (** Turn the scope into a span context *) - let[@inline] to_span_ctx (self : t) : Span_ctx.t = - Span_ctx.make ~trace_id:self.trace_id ~parent_id:self.span_id () - - (** 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. *) - let[@inline] add_event (scope : t) (ev : unit -> Event.t) : unit = - if Collector.has_backend () then scope.items <- Ev (ev (), scope.items) - - let[@inline] record_exception (scope : t) (exn : exn) - (bt : Printexc.raw_backtrace) : unit = - if Collector.has_backend () then ( - let ev = - Event.make "exception" - ~attrs: - [ - "message", `String (Printexc.to_string exn); - "type", `String (Printexc.exn_slot_name exn); - "stacktrace", `String (Printexc.raw_backtrace_to_string bt); - ] - in - scope.items <- Ev (ev, scope.items) - ) - - (** 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. *) - let[@inline] add_attrs (scope : t) (attrs : unit -> key_value list) : unit = - if Collector.has_backend () then - scope.items <- - List.fold_left (fun acc attr -> Attr (attr, acc)) scope.items (attrs ()) - - (** 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. *) - let[@inline] add_links (scope : t) (links : unit -> Span_link.t list) : unit = - if Collector.has_backend () then - scope.items <- - List.fold_left - (fun acc link -> Span_link (link, acc)) - scope.items (links ()) - - (** The opaque key necessary to access/set the ambient scope with - {!Ambient_context}. *) - let ambient_scope_key : t Ambient_context.key = Ambient_context.create_key () - - (** Obtain current scope from {!Ambient_context}, if available. *) - let get_ambient_scope ?scope () : t option = - match scope with - | Some _ -> scope - | None -> Ambient_context.get ambient_scope_key - - (** [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 *) - let[@inline] with_ambient_scope (sc : t) (f : unit -> 'a) : 'a = - Ambient_context.with_binding ambient_scope_key sc (fun _ -> f ()) -end - (** {2 Traces} *) (** Spans. @@ -1065,6 +907,169 @@ end = struct span, id end +(** Scopes. + + A scope is a trace ID and the span ID of the currently active span. +*) +module Scope : sig + type item_list + + type t = { + trace_id: Trace_id.t; + span_id: Span_id.t; + mutable items: item_list; + mutable span_status: Span.status option; + } + + val attrs : t -> key_value list + + val events : t -> Event.t list + + val links : t -> Span_link.t list + + val set_span_status : t -> Span.status -> unit + + val make : + trace_id:Trace_id.t -> + span_id:Span_id.t -> + ?events:Event.t list -> + ?attrs:key_value list -> + ?links:Span_link.t list -> + ?span_status:Span.status -> + unit -> + t + + val to_span_ctx : t -> Span_ctx.t + + val add_event : t -> (unit -> Event.t) -> unit + + val record_exception : t -> exn -> Printexc.raw_backtrace -> unit + + val add_attrs : t -> (unit -> key_value list) -> unit + + val add_links : t -> (unit -> Span_link.t list) -> unit + + val ambient_scope_key : t Ambient_context.key + + val get_ambient_scope : ?scope:t -> unit -> t option + + val with_ambient_scope : t -> (unit -> 'a) -> 'a +end = struct + type item_list = + | Nil + | Ev of Event.t * item_list + | Attr of key_value * item_list + | Span_link of Span_link.t * item_list + + type t = { + trace_id: Trace_id.t; + span_id: Span_id.t; + mutable items: item_list; + mutable span_status: Span.status option; + } + + let attrs scope = + let rec loop acc = function + | Nil -> acc + | Attr (attr, l) -> loop (attr :: acc) l + | Ev (_, l) | Span_link (_, 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_link (_, 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) | Attr (_, l) -> loop acc l + in + loop [] scope.items + + let set_span_status scope status = scope.span_status <- Some status + + let make ~trace_id ~span_id ?(events = []) ?(attrs = []) ?(links = []) + ?span_status () : t = + let items = + let items = List.fold_left (fun acc ev -> Ev (ev, acc)) Nil 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; span_status } + + (** Turn the scope into a span context *) + let[@inline] to_span_ctx (self : t) : Span_ctx.t = + Span_ctx.make ~trace_id:self.trace_id ~parent_id:self.span_id () + + (** 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. *) + let[@inline] add_event (scope : t) (ev : unit -> Event.t) : unit = + if Collector.has_backend () then scope.items <- Ev (ev (), scope.items) + + let[@inline] record_exception (scope : t) (exn : exn) + (bt : Printexc.raw_backtrace) : unit = + if Collector.has_backend () then ( + let ev = + Event.make "exception" + ~attrs: + [ + "message", `String (Printexc.to_string exn); + "type", `String (Printexc.exn_slot_name exn); + "stacktrace", `String (Printexc.raw_backtrace_to_string bt); + ] + in + scope.items <- Ev (ev, scope.items) + ) + + (** 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. *) + let[@inline] add_attrs (scope : t) (attrs : unit -> key_value list) : unit = + if Collector.has_backend () then + scope.items <- + List.fold_left (fun acc attr -> Attr (attr, acc)) scope.items (attrs ()) + + (** 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. *) + let[@inline] add_links (scope : t) (links : unit -> Span_link.t list) : unit = + if Collector.has_backend () then + scope.items <- + List.fold_left + (fun acc link -> Span_link (link, acc)) + scope.items (links ()) + + (** The opaque key necessary to access/set the ambient scope with + {!Ambient_context}. *) + let ambient_scope_key : t Ambient_context.key = Ambient_context.create_key () + + (** Obtain current scope from {!Ambient_context}, if available. *) + let get_ambient_scope ?scope () : t option = + match scope with + | Some _ -> scope + | None -> Ambient_context.get ambient_scope_key + + (** [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 *) + let[@inline] with_ambient_scope (sc : t) (f : unit -> 'a) : 'a = + Ambient_context.with_binding ambient_scope_key sc (fun _ -> f ()) +end + (** Traces. See {{: https://opentelemetry.io/docs/reference/specification/overview/#tracing-signal} the spec} *) @@ -1097,6 +1102,7 @@ module Trace = struct trace_id: Trace_id.t; span_id: Span_id.t; mutable items: Scope.item_list; + mutable span_status: Span.status option; } [@@deprecated "use Scope.t"] @@ -1133,13 +1139,16 @@ module Trace = struct (* called once we're done, to emit a span *) let finally res = let status = - match res with - | Ok () -> default_status ~code:Status_code_ok () - | Error (e, bt) -> - (* add backtrace *) - Scope.record_exception scope e bt; - default_status ~code:Status_code_error ~message:(Printexc.to_string e) - () + match scope.span_status with + | Some status -> status + | None -> + (match res with + | Ok () -> default_status ~code:Status_code_ok () + | Error (e, bt) -> + (* add backtrace *) + Scope.record_exception scope e bt; + default_status ~code:Status_code_error + ~message:(Printexc.to_string e) ()) in let span, _ = (* TODO: should the attrs passed to with_ go on the Span