trace-collector: Documentation

This commit is contained in:
Elliott Cable 2023-08-30 01:50:04 +00:00
parent 2b3e3d733c
commit 925c962945
4 changed files with 86 additions and 9 deletions

View file

@ -26,10 +26,11 @@ MIT
* [x] batching, perf, etc.
- [ ] async collector relying on ocurl-multi
- [ ] interface with `logs` (carry context around)
- [x] implicit scope (via [ambient-context][])
## Use
For now, instrument manually:
For now, instrument traces/spans, logs, and metrics manually:
```ocaml
module Otel = Opentelemetry
@ -45,17 +46,35 @@ let foo () =
]);
do_more_work();
()
```
### Setup
If you're writing a top-level application, you need to perform some initial configuration.
1. Set the [`service_name`][];
2. configure our [ambient-context][] dependency with the appropriate storage for your environment — TLS, Lwt, Eio ... (see [their docs][install-ambient-storage] for more details);
3. and install a [`Collector`][] (usually by calling your collector's `with_setup` function.)
For example, if your application is using Lwt, and you're using `ocurl` as your collector, you might do something like this:
```ocaml
let main () =
Otel.Globals.service_name := "my_service";
Otel.GC_metrics.basic_setup();
Ambient_context.with_storage_provider (Ambient_context_lwt.storage ()) @@ fun () ->
Opentelemetry_client_ocurl.with_setup () @@ fun () ->
(* … *)
foo ();
(* … *)
```
[`service_name`]: <https://v3.ocaml.org/p/opentelemetry/0.5/doc/Opentelemetry/Globals/index.html#val-service_name>
[`Collector`]: <https://v3.ocaml.org/p/opentelemetry/0.5/doc/Opentelemetry/Collector/index.html>
[ambient-context]: <https://v3.ocaml.org/p/ambient-context>
[install-ambient-storage]: <https://github.com/ELLIOTTCABLE/ocaml-ambient-context#-as-a-top-level-application>
## Configuration
The library is configurable via `Opentelemetry.Config`, via the standard

View file

@ -528,8 +528,11 @@ module Scope = struct
| Some _ -> scope
| None -> Ambient_context.get ambient_scope_key
(** [with_ambient_scope sc f] calls [f()] in a context where [sc] is the
(thread)-local scope, then reverts to the previous local scope, if any. *)
(** [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 <https://github.com/ELLIOTTCABLE/ocaml-ambient-context> 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
@ -761,12 +764,20 @@ module Trace = struct
(** Sync span guard.
@param force_new_trace_id if true (default false), the span will not use a
ambient scope, [scope], or [trace_id], but will always
create a fresh new trace ID.
Notably, this includes {e implicit} scope-tracking: if called without a
[~scope] argument (or [~parent]/[~trace_id]), it will check in the
{!Ambient_context} for a surrounding environment, and use that as the
scope. Similarly, it uses {!Scope.with_ambient_scope} to {e set} a new
scope in the ambient context, so that any logically-nested calls to
{!with_} will use this span as their parent.
{b NOTE} be careful not to call this inside a Gc alarm, as it can
cause deadlocks. *)
cause deadlocks.
@param force_new_trace_id if true (default false), the span will not use a
ambient scope, the [~scope] argument, nor [~trace_id], but will instead
always create fresh identifiers for this span *)
let with_ ?force_new_trace_id ?trace_state ?service_name ?attrs ?kind
?trace_id ?parent ?scope ?links name (cb : Scope.t -> 'a) : 'a =
let thunk, finally =

View file

@ -17,7 +17,6 @@ module Internal = struct
parent_scope: Otel.Scope.t option;
}
(** Table indexed by ocaml-trace spans *)
module Active_span_tbl = Hashtbl.Make (struct
include Int64

View file

@ -2,6 +2,16 @@ module Otel := Opentelemetry
module Otrace := Trace
module TLS := Ambient_context_tls.Thread_local
(** [ocaml-opentelemetry.trace] implements a {!Trace_core.Collector} for {{:https://v3.ocaml.org/p/trace} ocaml-trace}.
After installing this collector with {!setup}, you can consume libraries
that use ocaml-trace, and they will automatically emit OpenTelemetry spans
and logs.
Both explicit scope (in the [_manual] functions such as [enter_manual_span])
and implicit scope (in {!Internal.M.with_span}, via {!Ambient_context}) are
supported; see the detailed notes on {!Internal.M.enter_manual_span}. *)
val setup : unit -> unit
(** Install the OTEL backend as a Trace collector *)
@ -22,6 +32,14 @@ module Internal : sig
string (* span name *) ->
(Otrace.span -> 'a) ->
'a
(** Implements {!Trace_core.Collector.S.with_span}, with the OpenTelemetry
collector as the backend. Invoked via {!Trace.with_span}.
Notably, this has the same implicit-scope semantics as
{!Opentelemetry.Trace.with_}, and requires configuration of
{!Ambient_context}.
@see <https://github.com/ELLIOTTCABLE/ocaml-ambient-context> ambient-context docs *)
val enter_manual_span :
parent:Otrace.explicit_span option ->
@ -32,8 +50,37 @@ module Internal : sig
data:(string * Otrace.user_data) list ->
string (* span name *) ->
Otrace.explicit_span
(** Implements {!Trace_core.Collector.S.enter_manual_span}, with the OpenTelemetry
collector as the backend. Invoked at {!Trace.enter_manual_toplevel_span}
and {!Trace.enter_manual_sub_span}; requires an eventual call to
{!Trace.exit_manual_span}.
These 'manual span' functions {e do not} implement the same implicit-
scope semantics of {!with_span}; and thus don't need to wrap a single
stack-frame / callback; you can freely enter a span at any point, store
the returned {!Trace.explicit_span}, and exit it at any later point with
{!Trace.exit_manual_span}.
However, for that same reason, they also cannot update the
{!Ambient_context} that is, when you invoke the various [manual]
functions, if you then invoke other functions that use
{!Trace.with_span}, those callees {e will not} see the span you entered
manually as their [parent].
Generally, the best practice is to only use these [manual] functions at
the 'leaves' of your callstack: that is, don't invoke user callbacks
from within them; or if you do, make sure to pass the [explicit_span]
you recieve from this function onwards to the user callback, so they can create further
child-spans. *)
val exit_manual_span : Otrace.explicit_span -> unit
(** Implements {!Trace_core.Collector.S.exit_manual_span}, with the
OpenTelemetry collector as the backend. Invoked at
{!Trace.exit_manual_span}. Expects the [explicit_span] returned from an
earlier call to {!Trace.enter_manual_toplevel_span} or
{!Trace.enter_manual_sub_span}.
(See the notes at {!enter_manual_span} about {!Ambient_context}.) *)
val message :
?span:Otrace.span ->
@ -68,6 +115,7 @@ module Internal : sig
module Active_span_tbl : Hashtbl.S with type key = Otrace.span
(** Table indexed by ocaml-trace spans. *)
module Active_spans : sig
type t = private { tbl: span_begin Active_span_tbl.t } [@@unboxed]