mirror of
https://github.com/ocaml-tracing/ocaml-opentelemetry.git
synced 2026-03-08 03:47:59 -04:00
doc: migration guide, readme
This commit is contained in:
parent
e74f824134
commit
9095c2a506
2 changed files with 295 additions and 21 deletions
45
README.md
45
README.md
|
|
@ -7,10 +7,10 @@ connectors to talk to opentelemetry software such as [jaeger](https://www.jaeger
|
||||||
|
|
||||||
- library `opentelemetry` should be used to instrument your code
|
- library `opentelemetry` should be used to instrument your code
|
||||||
and possibly libraries. It doesn't communicate with anything except
|
and possibly libraries. It doesn't communicate with anything except
|
||||||
a backend (default: dummy backend);
|
an exporter (default: no-op);
|
||||||
- library `opentelemetry-client-ocurl` is a backend that communicates
|
- library `opentelemetry-client-ocurl` is an exporter that communicates
|
||||||
via http+protobuf with some collector (otelcol, datadog-agent, etc.) using cURL bindings;
|
via http+protobuf with some collector (otelcol, datadog-agent, etc.) using cURL bindings;
|
||||||
- library `opentelemetry-client-cohttp-lwt` is a backend that communicates
|
- library `opentelemetry-client-cohttp-lwt` is an exporter that communicates
|
||||||
via http+protobuf with some collector using cohttp.
|
via http+protobuf with some collector using cohttp.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
@ -39,14 +39,14 @@ module Otel = Opentelemetry
|
||||||
let (let@) = (@@)
|
let (let@) = (@@)
|
||||||
|
|
||||||
let foo () =
|
let foo () =
|
||||||
let@ scope = Otel.Trace.with_ "foo"
|
let@ span = Otel.Tracer.with_ "foo"
|
||||||
~attrs:["hello", `String "world"] in
|
~attrs:["hello", `String "world"] in
|
||||||
do_work();
|
do_work ();
|
||||||
Otel.Metrics.(
|
let now = Otel.Clock.now Otel.Meter.default.clock in
|
||||||
emit [
|
Otel.Meter.emit1 Otel.Meter.default
|
||||||
gauge ~name:"foo.x" [int 42];
|
Otel.Metrics.(gauge ~name:"foo.x" [int ~now 42]);
|
||||||
]);
|
Otel.Span.add_event span (Otel.Event.make "work done");
|
||||||
do_more_work();
|
do_more_work ();
|
||||||
()
|
()
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -56,14 +56,14 @@ If you're writing a top-level application, you need to perform some initial conf
|
||||||
|
|
||||||
1. Set the [`service_name`][];
|
1. Set the [`service_name`][];
|
||||||
2. optionally configure [ambient-context][] with the appropriate storage for your environment — TLS, Lwt, Eio…;
|
2. optionally configure [ambient-context][] with the appropriate storage for your environment — TLS, Lwt, Eio…;
|
||||||
3. and install a [`Collector`][] (usually by calling your collector's `with_setup` function.)
|
3. and install an exporter (usually by calling your client library'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:
|
For example, if your application is using Lwt, and you're using `ocurl` as your collector, you might do something like this:
|
||||||
|
|
||||||
```ocaml
|
```ocaml
|
||||||
let main () =
|
let main () =
|
||||||
Otel.Globals.service_name := "my_service";
|
Otel.Globals.service_name := "my_service";
|
||||||
Otel.GC_metrics.basic_setup();
|
Otel.Gc_metrics.setup ();
|
||||||
|
|
||||||
Opentelemetry_ambient_context.set_storage_provider (Opentelemetry_ambient_context_lwt.storage ());
|
Opentelemetry_ambient_context.set_storage_provider (Opentelemetry_ambient_context_lwt.storage ());
|
||||||
Opentelemetry_client_ocurl.with_setup () @@ fun () ->
|
Opentelemetry_client_ocurl.with_setup () @@ fun () ->
|
||||||
|
|
@ -72,10 +72,13 @@ let main () =
|
||||||
(* … *)
|
(* … *)
|
||||||
```
|
```
|
||||||
|
|
||||||
[`service_name`]: <https://v3.ocaml.org/p/opentelemetry/0.5/doc/Opentelemetry/Globals/index.html#val-service_name>
|
[`service_name`]: <https://v3.ocaml.org/p/opentelemetry/latest/doc/Opentelemetry/Globals/index.html#val-service_name>
|
||||||
[`Collector`]: <https://v3.ocaml.org/p/opentelemetry/0.5/doc/Opentelemetry/Collector/index.html>
|
|
||||||
[ambient-context]: now vendored as `opentelemetry.ambient-context`, formerly <https://v3.ocaml.org/p/ambient-context>
|
[ambient-context]: now vendored as `opentelemetry.ambient-context`, formerly <https://v3.ocaml.org/p/ambient-context>
|
||||||
|
|
||||||
|
## Migration v012 → v0.13
|
||||||
|
|
||||||
|
see `doc/migration_guide_v0.13.md`
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
### Environment Variables
|
### Environment Variables
|
||||||
|
|
@ -104,20 +107,20 @@ The library supports standard OpenTelemetry environment variables:
|
||||||
- `OTEL_EXPORTER_OTLP_LOGS_HEADERS` - logs-specific headers
|
- `OTEL_EXPORTER_OTLP_LOGS_HEADERS` - logs-specific headers
|
||||||
|
|
||||||
|
|
||||||
## Collector opentelemetry-client-ocurl
|
## opentelemetry-client-ocurl
|
||||||
|
|
||||||
This is a synchronous collector that uses the http+protobuf format
|
This is a synchronous exporter that uses the http+protobuf format
|
||||||
to send signals (metrics, traces, logs) to some other collector (eg. `otelcol`
|
to send signals (metrics, traces, logs) to some collector (eg. `otelcol`
|
||||||
or the datadog agent).
|
or the datadog agent).
|
||||||
|
|
||||||
Do note that this backend uses a thread pool and is incompatible
|
Do note that it uses a thread pool and is incompatible
|
||||||
with uses of `fork` on some Unixy systems.
|
with uses of `fork` on some Unixy systems.
|
||||||
See [#68](https://github.com/imandra-ai/ocaml-opentelemetry/issues/68) for a possible workaround.
|
See [#68](https://github.com/imandra-ai/ocaml-opentelemetry/issues/68) for a possible workaround.
|
||||||
|
|
||||||
## Collector opentelemetry-client-cohttp-lwt
|
## opentelemetry-client-cohttp-lwt
|
||||||
|
|
||||||
This is a Lwt-friendly collector that uses cohttp to send
|
This is a Lwt-friendly exporter that uses cohttp to send
|
||||||
signals to some other collector (e.g. `otelcol`). It must be run
|
signals to some collector (e.g. `otelcol`). It must be run
|
||||||
inside a `Lwt_main.run` scope.
|
inside a `Lwt_main.run` scope.
|
||||||
|
|
||||||
## Opentelemetry-trace
|
## Opentelemetry-trace
|
||||||
|
|
|
||||||
271
doc/migration_guide_v0.13.md
Normal file
271
doc/migration_guide_v0.13.md
Normal file
|
|
@ -0,0 +1,271 @@
|
||||||
|
# Migration guide: v0.12 → v0.13
|
||||||
|
|
||||||
|
This guide covers breaking changes when upgrading from v0.12.
|
||||||
|
|
||||||
|
## 1. Backend setup: `Collector` → `Sdk` + `Exporter`
|
||||||
|
|
||||||
|
v0.12 used a first-class module `BACKEND` installed into a global slot via
|
||||||
|
`Collector.set_backend`. v0.13 replaces this with a plain record `Exporter.t`
|
||||||
|
installed via `Sdk.set`.
|
||||||
|
|
||||||
|
The `with_setup` helper in each client library still exists, so if you use that
|
||||||
|
you mainly need to rename the module.
|
||||||
|
|
||||||
|
```ocaml
|
||||||
|
(* v0.12 *)
|
||||||
|
Opentelemetry_client_ocurl.with_setup ~config () (fun () ->
|
||||||
|
(* your code *)
|
||||||
|
())
|
||||||
|
|
||||||
|
(* v0.13: same call, internals changed; ~stop removed, ~after_shutdown added *)
|
||||||
|
Opentelemetry_client_ocurl.with_setup
|
||||||
|
~after_shutdown:(fun _exp -> ())
|
||||||
|
~config () (fun () ->
|
||||||
|
(* your code *)
|
||||||
|
())
|
||||||
|
```
|
||||||
|
|
||||||
|
If you called `setup`/`remove_backend` manually:
|
||||||
|
|
||||||
|
```ocaml
|
||||||
|
(* v0.12 *)
|
||||||
|
Opentelemetry_client_ocurl.setup ~config ()
|
||||||
|
(* ... *)
|
||||||
|
Opentelemetry_client_ocurl.remove_backend ()
|
||||||
|
|
||||||
|
(* v0.13 *)
|
||||||
|
Opentelemetry_client_ocurl.setup ~config ()
|
||||||
|
(* ... *)
|
||||||
|
Opentelemetry_client_ocurl.remove_exporter ()
|
||||||
|
```
|
||||||
|
|
||||||
|
The `~stop:bool Atomic.t` parameter has been removed from the ocurl client.
|
||||||
|
Use `Sdk.active ()` (an `Aswitch.t`) to detect shutdown instead.
|
||||||
|
|
||||||
|
## 2. `Trace.with_` → `Tracer.with_`, callback gets a `Span.t`
|
||||||
|
|
||||||
|
The most common migration. The module is renamed and the callback argument type
|
||||||
|
changes from `Scope.t` to `Span.t`.
|
||||||
|
|
||||||
|
```ocaml
|
||||||
|
(* v0.12 *)
|
||||||
|
Trace.with_ "my-op" ~attrs:["k", `String "v"] (fun (scope : Scope.t) ->
|
||||||
|
Scope.add_event scope (fun () -> Event.make "something happened");
|
||||||
|
Scope.add_attrs scope (fun () -> ["extra", `Int 42]);
|
||||||
|
do_work ()
|
||||||
|
)
|
||||||
|
|
||||||
|
(* v0.13 *)
|
||||||
|
Tracer.with_ "my-op" ~attrs:["k", `String "v"] (fun (span : Span.t) ->
|
||||||
|
Span.add_event span (Event.make "something happened");
|
||||||
|
Span.add_attrs span ["extra", `Int 42];
|
||||||
|
do_work ()
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
`Trace` is kept as a deprecated alias for `Tracer`.
|
||||||
|
|
||||||
|
Key differences on the callback argument:
|
||||||
|
|
||||||
|
| v0.12 (`Scope.t`) | v0.13 (`Span.t`) |
|
||||||
|
|--------------------------------------------|--------------------------------------|
|
||||||
|
| `scope.trace_id` | `Span.trace_id span` |
|
||||||
|
| `scope.span_id` | `Span.id span` |
|
||||||
|
| `Scope.add_event scope (fun () -> ev)` | `Span.add_event span ev` |
|
||||||
|
| `Scope.add_attrs scope (fun () -> attrs)` | `Span.add_attrs span attrs` |
|
||||||
|
| `Scope.set_status scope st` | `Span.set_status span st` |
|
||||||
|
| `Scope.record_exception scope e bt` | `Span.record_exception span e bt` |
|
||||||
|
| `Scope.to_span_ctx scope` | `Span.to_span_ctx span` |
|
||||||
|
| `Scope.to_span_link scope` | `Span.to_span_link span` |
|
||||||
|
| `~scope:scope` (pass parent explicitly) | `~parent:span` |
|
||||||
|
|
||||||
|
The `~scope` parameter of `Trace.with_` is renamed to `~parent`:
|
||||||
|
|
||||||
|
```ocaml
|
||||||
|
(* v0.12 *)
|
||||||
|
Trace.with_ "child" ~scope:parent_scope (fun child -> ...)
|
||||||
|
|
||||||
|
(* v0.13 *)
|
||||||
|
Tracer.with_ "child" ~parent:parent_span (fun child -> ...)
|
||||||
|
```
|
||||||
|
|
||||||
|
In addition, `Scope.t` is entirely removed because `Span.t` is now mutable.
|
||||||
|
For additional efficiency, `Span.t` is directly encodable to protobuf
|
||||||
|
without the need to allocate further intermediate structures.
|
||||||
|
|
||||||
|
## 3. `Logs` → `Logger`, new emit helpers
|
||||||
|
|
||||||
|
The `Logs` module is renamed to `Logger` (`Logs` is kept as a deprecated alias).
|
||||||
|
Direct construction of log records and batch-emit is replaced by convenience
|
||||||
|
functions.
|
||||||
|
|
||||||
|
```ocaml
|
||||||
|
(* v0.12 *)
|
||||||
|
Logs.emit [
|
||||||
|
Logs.make_str ~severity:Severity_number_warn "something went wrong"
|
||||||
|
]
|
||||||
|
|
||||||
|
Logs.emit [
|
||||||
|
Logs.make_strf ~severity:Severity_number_info "processed %d items" n
|
||||||
|
]
|
||||||
|
|
||||||
|
(* v0.13: simple string *)
|
||||||
|
Logger.log ~severity:Severity_number_warn "something went wrong"
|
||||||
|
|
||||||
|
(* v0.13: formatted *)
|
||||||
|
Logger.logf ~severity:Severity_number_info (fun k -> k "processed %d items" n)
|
||||||
|
```
|
||||||
|
|
||||||
|
If you need to keep the trace/span correlation:
|
||||||
|
|
||||||
|
```ocaml
|
||||||
|
(* v0.12 *)
|
||||||
|
Logs.emit [
|
||||||
|
Logs.make_str ~trace_id ~span_id ~severity:Severity_number_info "ok"
|
||||||
|
]
|
||||||
|
|
||||||
|
(* v0.13 *)
|
||||||
|
Logger.log ~trace_id ~span_id ~severity:Severity_number_info "ok"
|
||||||
|
```
|
||||||
|
|
||||||
|
`Log_record.make_str` / `Log_record.make` still exist if you need to build
|
||||||
|
records manually and emit them via a `Logger.t`.
|
||||||
|
|
||||||
|
## 4. `Metrics.emit` → emit via a `Meter`
|
||||||
|
|
||||||
|
In v0.12 `Metrics.emit` was a top-level function that sent directly to the
|
||||||
|
collector. In v0.13 metrics go through a `Meter.t`. For most code the change
|
||||||
|
is mechanical:
|
||||||
|
|
||||||
|
```ocaml
|
||||||
|
(* v0.12 *)
|
||||||
|
Metrics.emit [
|
||||||
|
Metrics.gauge ~name:"queue.depth" [ Metrics.int ~now depth ]
|
||||||
|
]
|
||||||
|
|
||||||
|
(* v0.13: Meter.default emits to the global provider *)
|
||||||
|
Meter.emit1 Meter.default
|
||||||
|
(Metrics.gauge ~name:"queue.depth" [ Metrics.int ~now depth ])
|
||||||
|
```
|
||||||
|
|
||||||
|
`now` is now obtained from the meter's clock rather than `Timestamp_ns.now_unix_ns ()`:
|
||||||
|
|
||||||
|
```ocaml
|
||||||
|
(* v0.12 *)
|
||||||
|
let now = Timestamp_ns.now_unix_ns () in
|
||||||
|
Metrics.emit [ Metrics.sum ~name:"counter" [ Metrics.int ~now n ] ]
|
||||||
|
|
||||||
|
(* v0.13 *)
|
||||||
|
let now = Clock.now Meter.default.clock in
|
||||||
|
Meter.emit1 Meter.default
|
||||||
|
(Metrics.sum ~name:"counter" [ Metrics.int ~now n ])
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. `Metrics_callbacks.register` → `Meter.add_cb`
|
||||||
|
|
||||||
|
```ocaml
|
||||||
|
(* v0.12 *)
|
||||||
|
Metrics_callbacks.register (fun () ->
|
||||||
|
[ Metrics.gauge ~name:"foo" [ Metrics.int ~now:... 42 ] ])
|
||||||
|
|
||||||
|
(* v0.13: callback now receives a clock *)
|
||||||
|
Meter.add_cb (fun ~clock () ->
|
||||||
|
let now = Clock.now clock in
|
||||||
|
[ Metrics.gauge ~name:"foo" [ Metrics.int ~now 42 ] ])
|
||||||
|
```
|
||||||
|
|
||||||
|
After registering callbacks you must tell the SDK to drive them:
|
||||||
|
|
||||||
|
```ocaml
|
||||||
|
(* v0.13: call once after setup to schedule periodic emission *)
|
||||||
|
Meter.add_to_main_exporter Meter.default
|
||||||
|
```
|
||||||
|
|
||||||
|
In v0.12 this was automatic once `Metrics_callbacks.register` was called.
|
||||||
|
|
||||||
|
## 6. `GC_metrics.basic_setup` signature unchanged, `setup` changed
|
||||||
|
|
||||||
|
`GC_metrics.basic_setup ()` still works. The module has been renamed
|
||||||
|
to `Gc_metrics`, but the former name persists as a deprecated alias.
|
||||||
|
|
||||||
|
If you called the lower-level `GC_metrics.setup exp` directly:
|
||||||
|
|
||||||
|
```ocaml
|
||||||
|
(* v0.12 *)
|
||||||
|
GC_metrics.setup exporter
|
||||||
|
(* or *)
|
||||||
|
GC_metrics.setup_on_main_exporter ()
|
||||||
|
|
||||||
|
(* v0.13 *)
|
||||||
|
Gc_metrics.setup () (* uses Meter.default *)
|
||||||
|
(* or with a specific meter: *)
|
||||||
|
Gc_metrics.setup ~meter:my_meter ()
|
||||||
|
```
|
||||||
|
|
||||||
|
`GC_metrics.setup_on_main_exporter` has been removed.
|
||||||
|
|
||||||
|
## 7. `Collector.on_tick` → `Sdk.add_on_tick_callback`
|
||||||
|
|
||||||
|
```ocaml
|
||||||
|
(* v0.12 *)
|
||||||
|
Collector.on_tick (fun () -> do_background_work ())
|
||||||
|
|
||||||
|
(* v0.13 *)
|
||||||
|
Sdk.add_on_tick_callback (fun () -> do_background_work ())
|
||||||
|
```
|
||||||
|
|
||||||
|
## 8. `?service_name` parameter removed
|
||||||
|
|
||||||
|
`Trace.with_`, `Logs.emit`, and `Metrics.emit` accepted a `?service_name`
|
||||||
|
override. This is no longer supported per-call; set it once globally:
|
||||||
|
|
||||||
|
```ocaml
|
||||||
|
(* v0.12 *)
|
||||||
|
Trace.with_ "op" ~service_name:"my-svc" (fun _ -> ...)
|
||||||
|
|
||||||
|
(* v0.13: set globally before setup *)
|
||||||
|
Opentelemetry.Globals.service_name := "my-svc"
|
||||||
|
Tracer.with_ "op" (fun _ -> ...)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 9. `create_backend` / `BACKEND` module type removed
|
||||||
|
|
||||||
|
If you held a reference to a backend module:
|
||||||
|
|
||||||
|
```ocaml
|
||||||
|
(* v0.12 *)
|
||||||
|
let (module B : Collector.BACKEND) =
|
||||||
|
Opentelemetry_client_ocurl.create_backend ~config ()
|
||||||
|
in
|
||||||
|
Collector.set_backend (module B)
|
||||||
|
|
||||||
|
(* v0.13 *)
|
||||||
|
let exp : Exporter.t =
|
||||||
|
Opentelemetry_client_ocurl.create_exporter ~config ()
|
||||||
|
in
|
||||||
|
Sdk.set exp
|
||||||
|
```
|
||||||
|
|
||||||
|
## 10. New features (no migration needed)
|
||||||
|
|
||||||
|
- **`Sdk.get_tracer/get_meter/get_logger`**: obtain a provider pre-stamped with
|
||||||
|
instrumentation-scope metadata (`~name`, `~version`, `~__MODULE__`).
|
||||||
|
- **`Trace_provider` / `Meter_provider` / `Log_provider`**: independent
|
||||||
|
per-signal providers; useful for testing or multi-backend setups.
|
||||||
|
- **`Dynamic_enricher`**: register callbacks that inject attributes into every
|
||||||
|
span and log record at creation time (wide events).
|
||||||
|
- **Batch**: much better handling of batching overall.
|
||||||
|
|
||||||
|
## Quick checklist
|
||||||
|
|
||||||
|
- [ ] `Trace.with_` → `Tracer.with_`; callback argument `Scope.t` → `Span.t`
|
||||||
|
- [ ] `Scope.add_event`/`add_attrs` → `Span.add_event`/`add_attrs` (no thunk wrapper)
|
||||||
|
- [ ] `~scope:` → `~parent:` in nested `with_` calls
|
||||||
|
- [ ] `Logs.emit [Logs.make_str ...]` → `Logger.log`/`Logger.logf`
|
||||||
|
- [ ] `Metrics.emit [...]` → `Meter.emit1 Meter.default ...`
|
||||||
|
- [ ] `Metrics_callbacks.register` → `Meter.add_cb` (+ call `Meter.add_to_main_exporter`)
|
||||||
|
- [ ] `GC_metrics.setup exp` → `Gc_metrics.setup ()`
|
||||||
|
- [ ] `Collector.on_tick` → `Sdk.add_on_tick_callback`
|
||||||
|
- [ ] Remove `?service_name` call-site overrides; set `Globals.service_name` once
|
||||||
|
- [ ] `create_backend` → `create_exporter`; `set_backend` → `Sdk.set`
|
||||||
|
- [ ] `~stop:bool Atomic.t` removed from ocurl client
|
||||||
Loading…
Add table
Reference in a new issue