ocaml-opentelemetry/src/client-ocurl/opentelemetry_client_ocurl.ml
2026-03-06 14:44:01 -05:00

127 lines
3.8 KiB
OCaml

(*
https://github.com/open-telemetry/oteps/blob/main/text/0035-opentelemetry-protocol.md
https://github.com/open-telemetry/oteps/blob/main/text/0099-otlp-http.md
*)
module Config = Config
module OTELC = Opentelemetry_client
module OTEL = Opentelemetry
open Common_
type error = OTELC.Export_error.t
open struct
module Notifier = Opentelemetry_client_sync.Notifier_sync
module IO = Opentelemetry_client_sync.Io_sync
end
module Httpc : OTELC.Generic_http_consumer.HTTPC with module IO = IO = struct
module IO = IO
type t = Ezcurl_core.t
let create () = Ezcurl.make ()
let cleanup = Ezcurl.delete
let send (self : t) ~url ~headers:user_headers ~decode (bod : string) :
('a, error) result =
let r =
let headers = user_headers in
Ezcurl.post ~client:self ~headers ~params:[] ~url ~content:(`String bod)
()
in
match r with
| Error (code, msg) ->
let err =
`Failure
(spf
"sending signals via http POST failed:\n\
\ %s\n\
\ curl code: %s\n\
\ url: %s\n\
%!"
msg (Curl.strerror code) url)
in
Error err
| Ok { code; body; _ } when code >= 200 && code < 300 ->
(match decode with
| `Ret x -> Ok x
| `Dec f ->
let dec = Pbrt.Decoder.of_string body in
(try Ok (f dec)
with e ->
let bt = Printexc.get_backtrace () in
Error
(`Failure
(spf "decoding failed with:\n%s\n%s" (Printexc.to_string e) bt))))
| Ok { code; body; _ } ->
let err =
OTELC.Export_error.decode_invalid_http_response ~url ~code body
in
Error err
end
module Consumer_impl = OTELC.Generic_http_consumer.Make (IO) (Notifier) (Httpc)
let consumer ?(config = Config.make ()) () :
Opentelemetry_client.Consumer.any_signal_l_builder =
let n_workers = max 2 (min 32 config.bg_threads) in
let ticker_task =
if config.ticker_thread then
Some (float config.ticker_interval_ms /. 1000.)
else
None
in
Consumer_impl.consumer ~override_n_workers:n_workers ~on_tick:OTEL.Sdk.tick
~ticker_task ~config:config.common ()
let create_exporter ?(config = Config.make ()) () : OTEL.Exporter.t =
let consumer = consumer ~config () in
let bq =
Opentelemetry_client_sync.Bounded_queue_sync.create
~high_watermark:OTELC.Bounded_queue.Defaults.high_watermark ()
in
OTELC.Exporter_queued.create ~clock:OTEL.Clock.ptime_clock ~q:bq ~consumer ()
let create_backend = create_exporter
let setup_ ~config () : OTEL.Exporter.t =
let exporter = create_exporter ~config () in
OTEL.Sdk.set ~traces:config.common.traces ~metrics:config.common.metrics
~logs:config.common.logs exporter;
Option.iter
(fun min_level -> OTEL.Self_debug.to_stderr ~min_level ())
config.common.log_level;
OTEL.Self_debug.log OTEL.Self_debug.Info (fun () ->
"opentelemetry: ocurl exporter installed");
OTELC.Self_trace.set_enabled config.common.self_trace;
if config.common.self_metrics then Opentelemetry.Sdk.setup_self_metrics ();
exporter
let remove_exporter () : unit =
let open Opentelemetry_client_sync in
(* used to wait *)
let sq = Sync_queue.create () in
OTEL.Sdk.remove () ~on_done:(fun () -> Sync_queue.push sq ());
Sync_queue.pop sq
let remove_backend = remove_exporter
let setup ?(config : Config.t = Config.make ()) ?(enable = true) () =
if enable && not config.common.sdk_disabled then
ignore (setup_ ~config () : OTEL.Exporter.t)
let with_setup ?(after_shutdown = ignore) ?(config : Config.t = Config.make ())
?(enable = true) () f =
if enable && not config.common.sdk_disabled then (
let exp = setup_ ~config () in
Fun.protect f ~finally:(fun () ->
remove_exporter ();
after_shutdown exp)
) else
f ()