From d57c182daabd0610c42db13662d08fb5e77d2591 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 6 Mar 2026 14:42:39 -0500 Subject: [PATCH] add config parameter for `self_metrics` --- .../opentelemetry_client_cohttp_eio.ml | 3 +- .../opentelemetry_client_cohttp_lwt.ml | 1 + .../opentelemetry_client_ocurl_lwt.ml | 1 + .../opentelemetry_client_ocurl.ml | 1 + src/client/exporter_config.ml | 17 +++++++---- src/client/exporter_config.mli | 5 ++++ src/lib/sdk.ml | 30 +++++++++++++++++-- tests/client/test_client_lib.ml | 2 +- 8 files changed, 50 insertions(+), 10 deletions(-) diff --git a/src/client-cohttp-eio/opentelemetry_client_cohttp_eio.ml b/src/client-cohttp-eio/opentelemetry_client_cohttp_eio.ml index a01eb67e..5bcdc46e 100644 --- a/src/client-cohttp-eio/opentelemetry_client_cohttp_eio.ml +++ b/src/client-cohttp-eio/opentelemetry_client_cohttp_eio.ml @@ -186,7 +186,8 @@ let setup_ ~sw ~config env : unit = Opentelemetry.Self_debug.log Opentelemetry.Self_debug.Info (fun () -> "opentelemetry: cohttp-eio exporter installed"); - Opentelemetry_client.Self_trace.set_enabled config.self_trace + Opentelemetry_client.Self_trace.set_enabled config.self_trace; + if config.self_metrics then Opentelemetry.Sdk.setup_self_metrics () let setup ?(config = Config.make ()) ?(enable = true) ~sw env = if enable && not config.sdk_disabled then setup_ ~sw ~config env diff --git a/src/client-cohttp-lwt/opentelemetry_client_cohttp_lwt.ml b/src/client-cohttp-lwt/opentelemetry_client_cohttp_lwt.ml index 7bbb3430..a6e87671 100644 --- a/src/client-cohttp-lwt/opentelemetry_client_cohttp_lwt.ml +++ b/src/client-cohttp-lwt/opentelemetry_client_cohttp_lwt.ml @@ -120,6 +120,7 @@ let setup_ ~config () : unit = Opentelemetry.Self_debug.log Opentelemetry.Self_debug.Info (fun () -> "opentelemetry: cohttp-lwt exporter installed"); Opentelemetry_client.Self_trace.set_enabled config.self_trace; + if config.self_metrics then Opentelemetry.Sdk.setup_self_metrics (); () let setup ?(config = Config.make ()) ?(enable = true) () = diff --git a/src/client-ocurl-lwt/opentelemetry_client_ocurl_lwt.ml b/src/client-ocurl-lwt/opentelemetry_client_ocurl_lwt.ml index 9c022828..8d10d404 100644 --- a/src/client-ocurl-lwt/opentelemetry_client_ocurl_lwt.ml +++ b/src/client-ocurl-lwt/opentelemetry_client_ocurl_lwt.ml @@ -95,6 +95,7 @@ let setup_ ~config () : Exporter.t = Opentelemetry.Self_debug.log Opentelemetry.Self_debug.Info (fun () -> "opentelemetry: ocurl-lwt exporter installed"); Opentelemetry_client.Self_trace.set_enabled config.self_trace; + if config.self_metrics then Opentelemetry.Sdk.setup_self_metrics (); exp diff --git a/src/client-ocurl/opentelemetry_client_ocurl.ml b/src/client-ocurl/opentelemetry_client_ocurl.ml index bc75df06..d8cd316e 100644 --- a/src/client-ocurl/opentelemetry_client_ocurl.ml +++ b/src/client-ocurl/opentelemetry_client_ocurl.ml @@ -100,6 +100,7 @@ let setup_ ~config () : OTEL.Exporter.t = "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 = diff --git a/src/client/exporter_config.ml b/src/client/exporter_config.ml index 39f7ae3c..07e23e8f 100644 --- a/src/client/exporter_config.ml +++ b/src/client/exporter_config.ml @@ -26,6 +26,7 @@ type t = { metrics: Opentelemetry.Provider_config.t; logs: Opentelemetry.Provider_config.t; self_trace: bool; + self_metrics: bool; http_concurrency_level: int option; retry_max_attempts: int; retry_initial_delay_ms: float; @@ -65,6 +66,7 @@ let pp out (self : t) : unit = log_level; sdk_disabled; self_trace; + self_metrics; url_traces; url_metrics; url_logs; @@ -91,7 +93,8 @@ let pp out (self : t) : unit = in Format.fprintf out "{@[ debug=%B;@ log_level=%a;@ sdk_disabled=%B;@ self_trace=%B;@ \ - url_traces=%S;@ url_metrics=%S;@ url_logs=%S;@ @[<2>headers=@,\ + self_metrics=%B;@ url_traces=%S;@ url_metrics=%S;@ url_logs=%S;@ \ + @[<2>headers=@,\ %a@];@ @[<2>headers_traces=@,\ %a@];@ @[<2>headers_metrics=@,\ %a@];@ @[<2>headers_logs=@,\ @@ -100,8 +103,8 @@ let pp out (self : t) : unit = logs=%a;@ http_concurrency_level=%a;@ retry_max_attempts=%d;@ \ retry_initial_delay_ms=%.0f;@ retry_max_delay_ms=%.0f;@ \ retry_backoff_multiplier=%.1f @]}" - debug pp_log_level log_level sdk_disabled self_trace url_traces url_metrics - url_logs ppheaders headers ppheaders headers_traces ppheaders + debug pp_log_level log_level sdk_disabled self_trace self_metrics url_traces + url_metrics url_logs ppheaders headers ppheaders headers_traces ppheaders headers_metrics ppheaders headers_logs pp_protocol protocol timeout_ms timeout_traces_ms timeout_metrics_ms timeout_logs_ms pp_provider_config traces pp_provider_config metrics pp_provider_config logs ppiopt @@ -135,6 +138,7 @@ type 'k make = ?timeout_metrics_ms:int -> ?timeout_logs_ms:int -> ?self_trace:bool -> + ?self_metrics:bool -> ?http_concurrency_level:int -> ?retry_max_attempts:int -> ?retry_initial_delay_ms:float -> @@ -245,9 +249,9 @@ module Env () : ENV = struct ?(protocol = get_protocol_from_env "OTEL_EXPORTER_OTLP_PROTOCOL") ?(timeout_ms = get_timeout_from_env "OTEL_EXPORTER_OTLP_TIMEOUT" 10_000) ?timeout_traces_ms ?timeout_metrics_ms ?timeout_logs_ms - ?(self_trace = false) ?http_concurrency_level ?(retry_max_attempts = 3) - ?(retry_initial_delay_ms = 100.) ?(retry_max_delay_ms = 5000.) - ?(retry_backoff_multiplier = 2.0) = + ?(self_trace = false) ?(self_metrics = false) ?http_concurrency_level + ?(retry_max_attempts = 3) ?(retry_initial_delay_ms = 100.) + ?(retry_max_delay_ms = 5000.) ?(retry_backoff_multiplier = 2.0) = let batch_timeout_ = Mtime.Span.(batch_timeout_ms * ms) in let traces = match traces with @@ -375,6 +379,7 @@ module Env () : ENV = struct metrics; logs; self_trace; + self_metrics; http_concurrency_level; retry_max_attempts; retry_initial_delay_ms; diff --git a/src/client/exporter_config.mli b/src/client/exporter_config.mli index 68f4ed27..d042f210 100644 --- a/src/client/exporter_config.mli +++ b/src/client/exporter_config.mli @@ -72,6 +72,10 @@ type t = { (** If true, the OTEL library will perform some self-instrumentation. Default [false]. @since 0.7 *) + self_metrics: bool; + (** If true, the OTEL library will regularly emit metrics about itself. + Default [false]. + @since NEXT_RELEASE *) http_concurrency_level: int option; (** How many HTTP requests can be done simultaneously (at most)? This can be used to represent the size of a pool of workers where each worker @@ -123,6 +127,7 @@ type 'k make = ?timeout_metrics_ms:int -> ?timeout_logs_ms:int -> ?self_trace:bool -> + ?self_metrics:bool -> ?http_concurrency_level:int -> ?retry_max_attempts:int -> ?retry_initial_delay_ms:float -> diff --git a/src/lib/sdk.ml b/src/lib/sdk.ml index 1f7db854..d4e6bae2 100644 --- a/src/lib/sdk.ml +++ b/src/lib/sdk.ml @@ -90,9 +90,35 @@ let set ?(traces = Provider_config.default) ?(metrics = Provider_config.default) Log_provider.set logger let self_metrics () : Metrics.t list = + let now = Clock.now_main () in + let emitter_metrics = + Emitter.self_metrics (Trace_provider.get ()).emit ~now + @ Emitter.self_metrics (Meter_provider.get ()).emit ~now + @ Emitter.self_metrics (Log_provider.get ()).emit ~now + in match get () with - | None -> [] - | Some exp -> exp.Exporter.self_metrics () + | None -> emitter_metrics + | Some exp -> exp.Exporter.self_metrics () @ emitter_metrics + +open struct + let self_metrics_enabled = Atomic.make false +end + +(** Regularly emit metrics about the OTEL SDK. Idempotent. *) +let setup_self_metrics () = + if not (Atomic.exchange self_metrics_enabled true) then ( + Self_debug.log Info (fun () -> "enabling self metrics"); + let interval_limiter = + Interval_limiter.create ~min_interval:Mtime.Span.(10 * s) () + in + let on_tick () = + if Interval_limiter.make_attempt interval_limiter then ( + let ms = self_metrics () in + Meter_provider.emit_l ms + ) + in + Globals.add_on_tick_callback on_tick + ) (* Permanent tick callback to drive batch timeouts on provider emitters *) let () = diff --git a/tests/client/test_client_lib.ml b/tests/client/test_client_lib.ml index 9f115d9a..1bb520fc 100644 --- a/tests/client/test_client_lib.ml +++ b/tests/client/test_client_lib.ml @@ -8,7 +8,7 @@ let test_config_printing () = in let expected = "{ debug=false; log_level=info; sdk_disabled=false; self_trace=false;\n\ - \ url_traces=\"http://localhost:4318/v1/traces\";\n\ + \ self_metrics=false; url_traces=\"http://localhost:4318/v1/traces\";\n\ \ url_metrics=\"http://localhost:4318/v1/metrics\";\n\ \ url_logs=\"http://localhost:4318/v1/logs\"; headers=[]; headers_traces=[];\n\ \ headers_metrics=[]; headers_logs=[]; protocol=http/protobuf;\n\