mirror of
https://github.com/ocaml-tracing/ocaml-opentelemetry.git
synced 2026-03-09 12:23:32 -04:00
commit
b52d7611a6
3 changed files with 106 additions and 10 deletions
|
|
@ -124,6 +124,7 @@ module type EMITTER = sig
|
||||||
|
|
||||||
val push_trace : Trace.resource_spans list -> unit
|
val push_trace : Trace.resource_spans list -> unit
|
||||||
val push_metrics : Metrics.resource_metrics list -> unit
|
val push_metrics : Metrics.resource_metrics list -> unit
|
||||||
|
val set_on_tick_callbacks : (unit -> unit) list ref -> unit
|
||||||
|
|
||||||
val tick : unit -> unit
|
val tick : unit -> unit
|
||||||
val cleanup : unit -> unit
|
val cleanup : unit -> unit
|
||||||
|
|
@ -200,6 +201,9 @@ let mk_emitter ~(config:Config.t) () : (module EMITTER) =
|
||||||
|
|
||||||
let ((module C) as curl) = (module Curl() : CURL) in
|
let ((module C) as curl) = (module Curl() : CURL) in
|
||||||
|
|
||||||
|
let on_tick_cbs_ = ref (ref []) in
|
||||||
|
let set_on_tick_callbacks = (:=) on_tick_cbs_ in
|
||||||
|
|
||||||
let send_metrics_http (l:Metrics.resource_metrics list list) =
|
let send_metrics_http (l:Metrics.resource_metrics list list) =
|
||||||
Pbrt.Encoder.reset encoder;
|
Pbrt.Encoder.reset encoder;
|
||||||
let resource_metrics =
|
let resource_metrics =
|
||||||
|
|
@ -332,6 +336,12 @@ let mk_emitter ~(config:Config.t) () : (module EMITTER) =
|
||||||
|
|
||||||
let tick() =
|
let tick() =
|
||||||
if Atomic.get needs_gc_metrics then sample_gc_metrics();
|
if Atomic.get needs_gc_metrics then sample_gc_metrics();
|
||||||
|
List.iter
|
||||||
|
(fun f ->
|
||||||
|
try f()
|
||||||
|
with e ->
|
||||||
|
Printf.eprintf "on tick callback raised: %s\n" (Printexc.to_string e))
|
||||||
|
!(!on_tick_cbs_);
|
||||||
if batch_timeout() then wakeup()
|
if batch_timeout() then wakeup()
|
||||||
in
|
in
|
||||||
|
|
||||||
|
|
@ -354,6 +364,7 @@ let mk_emitter ~(config:Config.t) () : (module EMITTER) =
|
||||||
let push_metrics e =
|
let push_metrics e =
|
||||||
E_metrics.push e;
|
E_metrics.push e;
|
||||||
if batch_timeout() then wakeup()
|
if batch_timeout() then wakeup()
|
||||||
|
let set_on_tick_callbacks = set_on_tick_callbacks
|
||||||
let tick=tick
|
let tick=tick
|
||||||
let cleanup () =
|
let cleanup () =
|
||||||
continue := false;
|
continue := false;
|
||||||
|
|
@ -384,6 +395,8 @@ let mk_emitter ~(config:Config.t) () : (module EMITTER) =
|
||||||
E_metrics.push e;
|
E_metrics.push e;
|
||||||
if batch_timeout() then emit_all_force()
|
if batch_timeout() then emit_all_force()
|
||||||
|
|
||||||
|
let set_on_tick_callbacks = set_on_tick_callbacks
|
||||||
|
|
||||||
let tick () =
|
let tick () =
|
||||||
if Atomic.get needs_gc_metrics then sample_gc_metrics();
|
if Atomic.get needs_gc_metrics then sample_gc_metrics();
|
||||||
if batch_timeout() then emit_all_force()
|
if batch_timeout() then emit_all_force()
|
||||||
|
|
@ -454,7 +467,7 @@ end
|
||||||
let setup_ ~(config:Config.t) () =
|
let setup_ ~(config:Config.t) () =
|
||||||
debug_ := config.debug;
|
debug_ := config.debug;
|
||||||
let module B = Backend(struct let config=config end)() in
|
let module B = Backend(struct let config=config end)() in
|
||||||
Opentelemetry.Collector.backend := Some (module B);
|
Opentelemetry.Collector.set_backend (module B);
|
||||||
B.cleanup
|
B.cleanup
|
||||||
|
|
||||||
let setup ?(config=Config.make()) ?(enable=true) () =
|
let setup ?(config=Config.make()) ?(enable=true) () =
|
||||||
|
|
|
||||||
|
|
@ -115,16 +115,36 @@ module Collector = struct
|
||||||
(** Should be called regularly for background processing,
|
(** Should be called regularly for background processing,
|
||||||
timeout checks, etc. *)
|
timeout checks, etc. *)
|
||||||
|
|
||||||
|
val set_on_tick_callbacks : (unit -> unit) list ref -> unit
|
||||||
|
(** Give the collector the list of callbacks to be executed
|
||||||
|
when [tick()] is called. Each such callback should be short and
|
||||||
|
reentrant. Depending on the collector's implementation, it might be
|
||||||
|
called from a thread that is not the one that called [on_tick]. *)
|
||||||
|
|
||||||
val cleanup : unit -> unit
|
val cleanup : unit -> unit
|
||||||
end
|
end
|
||||||
|
|
||||||
type backend = (module BACKEND)
|
type backend = (module BACKEND)
|
||||||
|
|
||||||
let backend : backend option ref = ref None
|
(* hidden *)
|
||||||
|
open struct
|
||||||
|
let on_tick_cbs_ = ref []
|
||||||
|
|
||||||
|
let backend : backend option ref = ref None
|
||||||
|
end
|
||||||
|
|
||||||
|
(** Set collector backend *)
|
||||||
|
let set_backend (b:backend) : unit =
|
||||||
|
let (module B) = b in
|
||||||
|
B.set_on_tick_callbacks on_tick_cbs_;
|
||||||
|
backend := Some b
|
||||||
|
|
||||||
(** Is there a configured backend? *)
|
(** Is there a configured backend? *)
|
||||||
let[@inline] has_backend () : bool = !backend != None
|
let[@inline] has_backend () : bool = !backend != None
|
||||||
|
|
||||||
|
(** Current backend, if any *)
|
||||||
|
let[@inline] get_backend () : backend option = !backend
|
||||||
|
|
||||||
let send_trace (l:Trace.resource_spans list) ~ret =
|
let send_trace (l:Trace.resource_spans list) ~ret =
|
||||||
match !backend with
|
match !backend with
|
||||||
| None -> ret()
|
| None -> ret()
|
||||||
|
|
@ -145,6 +165,8 @@ module Collector = struct
|
||||||
| None -> Bytes.make 8 '?'
|
| None -> Bytes.make 8 '?'
|
||||||
| Some (module B) -> B.rand_bytes_8()
|
| Some (module B) -> B.rand_bytes_8()
|
||||||
|
|
||||||
|
let on_tick f = on_tick_cbs_ := f :: !on_tick_cbs_
|
||||||
|
|
||||||
(** Do background work. Call this regularly if the collector doesn't
|
(** Do background work. Call this regularly if the collector doesn't
|
||||||
already have a ticker thread or internal timer. *)
|
already have a ticker thread or internal timer. *)
|
||||||
let tick () =
|
let tick () =
|
||||||
|
|
@ -580,9 +602,15 @@ end
|
||||||
module Metrics = struct
|
module Metrics = struct
|
||||||
open Metrics_types
|
open Metrics_types
|
||||||
|
|
||||||
|
(** A single metric, measuring some time-varying quantity or statistical
|
||||||
|
distribution. It is composed of one or more data points that have
|
||||||
|
precise values and time stamps. Each distinct metric should have a
|
||||||
|
distinct name. *)
|
||||||
type t = Metrics_types.metric
|
type t = Metrics_types.metric
|
||||||
|
|
||||||
let _program_start = Timestamp_ns.now_unix_ns()
|
open struct
|
||||||
|
let _program_start = Timestamp_ns.now_unix_ns()
|
||||||
|
end
|
||||||
|
|
||||||
(** Number data point, as a float *)
|
(** Number data point, as a float *)
|
||||||
let float ?(start_time_unix_nano=_program_start)
|
let float ?(start_time_unix_nano=_program_start)
|
||||||
|
|
@ -625,15 +653,34 @@ module Metrics = struct
|
||||||
~aggregation_temporality ()) in
|
~aggregation_temporality ()) in
|
||||||
default_metric ~name ?description ?unit_ ~data ()
|
default_metric ~name ?description ?unit_ ~data ()
|
||||||
|
|
||||||
(* TODO
|
(** Histogram data
|
||||||
|
@param count number of values in population (non negative)
|
||||||
|
@param sum sum of values in population (0 if count is 0)
|
||||||
|
@param bucket_counts count value of histogram for each bucket. Sum of
|
||||||
|
the counts must be equal to [count].
|
||||||
|
length must be [1+length explicit_bounds]
|
||||||
|
@param explicit_bounds strictly increasing list of bounds for the buckets *)
|
||||||
|
let histogram_data_point
|
||||||
|
?(start_time_unix_nano=_program_start)
|
||||||
|
?(now=Timestamp_ns.now_unix_ns())
|
||||||
|
?(attrs=[])
|
||||||
|
?(exemplars=[])
|
||||||
|
?(explicit_bounds=[])
|
||||||
|
?sum
|
||||||
|
~bucket_counts
|
||||||
|
~count
|
||||||
|
() : histogram_data_point =
|
||||||
|
let attributes = attrs |> List.map _conv_key_value in
|
||||||
|
default_histogram_data_point ~start_time_unix_nano ~time_unix_nano:now
|
||||||
|
~attributes ~exemplars ~bucket_counts ~explicit_bounds ~count ?sum ()
|
||||||
|
|
||||||
let histogram ~name ?description ?unit_
|
let histogram ~name ?description ?unit_
|
||||||
?aggregation_temporality
|
?aggregation_temporality
|
||||||
(l:number_data_point list) : t =
|
(l:histogram_data_point list) : t =
|
||||||
let data h=
|
let data =
|
||||||
Histogram (default_histogram ~data_points:l
|
Histogram (default_histogram ~data_points:l
|
||||||
?aggregation_temporality ()) in
|
?aggregation_temporality ()) in
|
||||||
default_metric ~name ?description ?unit_ ~data ()
|
default_metric ~name ?description ?unit_ ~data ()
|
||||||
*)
|
|
||||||
|
|
||||||
(* TODO: exponential history *)
|
(* TODO: exponential history *)
|
||||||
(* TODO: summary *)
|
(* TODO: summary *)
|
||||||
|
|
@ -658,6 +705,32 @@ module Metrics = struct
|
||||||
Collector.send_metrics [rm] ~ret:ignore
|
Collector.send_metrics [rm] ~ret:ignore
|
||||||
end
|
end
|
||||||
|
|
||||||
|
(** A set of callbacks that produce metrics when called.
|
||||||
|
|
||||||
|
The metrics are automatically called regularly.
|
||||||
|
|
||||||
|
This allows applications to register metrics callbacks from various points
|
||||||
|
in the program (or even in libraries), and not worry about setting
|
||||||
|
alarms/intervals to emit them. *)
|
||||||
|
module Metrics_callbacks = struct
|
||||||
|
open struct
|
||||||
|
let cbs_ : (unit -> Metrics.t list) list ref = ref []
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
(** [register f] adds the callback [f] to the list.
|
||||||
|
[f] will be called at unspecified times and is expected to return
|
||||||
|
a list of metrics. *)
|
||||||
|
let register f : unit =
|
||||||
|
if !cbs_ = [] then (
|
||||||
|
(* make sure we call [f] (and others) at each tick *)
|
||||||
|
Collector.on_tick (fun () ->
|
||||||
|
let m = List.map (fun f -> f()) !cbs_ |> List.flatten in
|
||||||
|
Metrics.emit m)
|
||||||
|
);
|
||||||
|
cbs_ := f :: !cbs_
|
||||||
|
end
|
||||||
|
|
||||||
module Logs = struct
|
module Logs = struct
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
@ -759,12 +832,13 @@ end = struct
|
||||||
let get_runtime_attributes () = Lazy.force runtime_attributes
|
let get_runtime_attributes () = Lazy.force runtime_attributes
|
||||||
|
|
||||||
let basic_setup () =
|
let basic_setup () =
|
||||||
let trigger() =
|
(* emit metrics when GC is called *)
|
||||||
match !Collector.backend with
|
let on_gc() =
|
||||||
|
match Collector.get_backend() with
|
||||||
| None -> ()
|
| None -> ()
|
||||||
| Some (module C) -> C.signal_emit_gc_metrics()
|
| Some (module C) -> C.signal_emit_gc_metrics()
|
||||||
in
|
in
|
||||||
ignore (Gc.create_alarm trigger : Gc.alarm)
|
ignore (Gc.create_alarm on_gc : Gc.alarm)
|
||||||
|
|
||||||
let bytes_per_word = Sys.word_size / 8
|
let bytes_per_word = Sys.word_size / 8
|
||||||
let word_to_bytes n = n * bytes_per_word
|
let word_to_bytes n = n * bytes_per_word
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,15 @@ let (let@) f x = f x
|
||||||
|
|
||||||
let sleep_inner = ref 0.1
|
let sleep_inner = ref 0.1
|
||||||
let sleep_outer = ref 2.0
|
let sleep_outer = ref 2.0
|
||||||
|
let num_sleep = ref 0
|
||||||
|
|
||||||
let run () =
|
let run () =
|
||||||
Printf.printf "collector is on %S\n%!" (Opentelemetry_client_ocurl.get_url());
|
Printf.printf "collector is on %S\n%!" (Opentelemetry_client_ocurl.get_url());
|
||||||
T.GC_metrics.basic_setup();
|
T.GC_metrics.basic_setup();
|
||||||
|
|
||||||
|
T.Metrics_callbacks.register (fun () ->
|
||||||
|
T.Metrics.[ sum ~name:"num-sleep" ~is_monotonic:true [int !num_sleep] ]);
|
||||||
|
|
||||||
let i = ref 0 in
|
let i = ref 0 in
|
||||||
while true do
|
while true do
|
||||||
let@ scope =
|
let@ scope =
|
||||||
|
|
@ -22,7 +26,9 @@ let run () =
|
||||||
let@ scope = T.Trace.with_ ~kind:T.Span.Span_kind_internal ~scope
|
let@ scope = T.Trace.with_ ~kind:T.Span.Span_kind_internal ~scope
|
||||||
~attrs:["j", `Int j]
|
~attrs:["j", `Int j]
|
||||||
"loop.inner" in
|
"loop.inner" in
|
||||||
|
|
||||||
Unix.sleepf !sleep_outer;
|
Unix.sleepf !sleep_outer;
|
||||||
|
incr num_sleep;
|
||||||
|
|
||||||
incr i;
|
incr i;
|
||||||
|
|
||||||
|
|
@ -33,7 +39,10 @@ let run () =
|
||||||
(* allocate some stuff *)
|
(* allocate some stuff *)
|
||||||
let _arr = Sys.opaque_identity @@ Array.make (25 * 25551) 42.0 in
|
let _arr = Sys.opaque_identity @@ Array.make (25 * 25551) 42.0 in
|
||||||
ignore _arr;
|
ignore _arr;
|
||||||
|
|
||||||
Unix.sleepf !sleep_inner;
|
Unix.sleepf !sleep_inner;
|
||||||
|
incr num_sleep;
|
||||||
|
|
||||||
if j=4 && !i mod 13 = 0 then failwith "oh no"; (* simulate a failure *)
|
if j=4 && !i mod 13 = 0 then failwith "oh no"; (* simulate a failure *)
|
||||||
|
|
||||||
T.Trace.add_event scope (fun()->T.Event.make "done with alloc");
|
T.Trace.add_event scope (fun()->T.Event.make "done with alloc");
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue