Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion devkit.opam
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,6 @@ depopts: [
]
conflicts: [
"jemalloc" {< "0.2"}
"opentelemetry" {< "0.13"}
"opentelemetry" {< "0.92"}
]
available: arch != "arm32" & arch != "x86_32"
12 changes: 11 additions & 1 deletion possibly_otel.mli
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
module Otrace := Trace_core

module Trace_context : sig
val header_names : string list
(** Names of the W3C trace context headers ([traceparent], [tracestate]) *)

val get_ambient_headers : ?explicit_span:Trace_core.span -> unit -> (string * string) list
(** Headers propagating the context of [explicit_span], or else of the ambient span:
[traceparent] with the span's sampled flag, and [tracestate] if non-empty.
Empty if there is no OTEL span. *)
end

module Traceparent : sig
val name : string
val get_ambient : ?explicit_span:Trace_core.span -> unit -> string option
end
end [@@deprecated "use Trace_context, which also propagates tracestate"]

val enter_manual_span :
__FUNCTION__:string ->
Expand Down
34 changes: 24 additions & 10 deletions possibly_otel.real.ml
Original file line number Diff line number Diff line change
@@ -1,18 +1,32 @@
open Opentelemetry

let ambient_span ?explicit_span () =
match explicit_span, Trace_core.current_span () with
| Some (Opentelemetry_trace.Extensions.Span_otel span), _
| _, Some (Opentelemetry_trace.Extensions.Span_otel span) -> Some span
| _ -> None

module Trace_context = struct
let header_names = Opentelemetry.Trace_context.[ Traceparent.name; Tracestate.name ]

let get_ambient_headers ?explicit_span () =
match ambient_span ?explicit_span () with
| Some span -> Opentelemetry.Trace_context.headers_of_span_ctx (Span.to_span_ctx span)
| None -> []
end

module Traceparent = struct
let name = Trace_context.Traceparent.name
let name = Opentelemetry.Trace_context.Traceparent.name

let get_ambient ?explicit_span () =
match explicit_span, Trace_core.current_span () with
| Some (Opentelemetry_trace.Extensions.Span_otel span), _
| _, Some (Opentelemetry_trace.Extensions.Span_otel span) ->
let ctx = Trace_context.Traceparent.to_value
~trace_id:(Span.trace_id span) ~parent_id:(Span.id span) () in
Some ctx
| _ -> None
match ambient_span ?explicit_span () with
| Some span ->
Some (Opentelemetry.Trace_context.Traceparent.to_value
~trace_flags:(Span.trace_flags span) ~trace_id:(Span.trace_id span) ~parent_id:(Span.id span) ())
| None -> None
end

(* no explicit [~parent]: the current span if any, otherwise let the collector look for
an ambient context (e.g. from an incoming traceparent) instead of forcing a new trace *)
let enter_manual_span ~__FUNCTION__ ~__FILE__ ~__LINE__ ?data name =
let parent = Trace_core.current_span () in
Trace_core.enter_span ~parent ~__FUNCTION__ ~__FILE__ ~__LINE__ ?data name
Trace_core.enter_span ~__FUNCTION__ ~__FILE__ ~__LINE__ ?data name
6 changes: 6 additions & 0 deletions possibly_otel.stub.ml
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
module Trace_context = struct
let header_names = [ "traceparent"; "tracestate" ]

let get_ambient_headers ?explicit_span:_ () = []
end

module Traceparent = struct
let name = "traceparent"

Expand Down
28 changes: 19 additions & 9 deletions web.ml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ module type CURL = sig
val perform : Curl.t -> Curl.curlCode t
end

(** [?headers] are sent as given, with one exception: if the request has an OpenTelemetry span, any [traceparent]
and [tracestate] headers are removed (names match case-insensitively) and replaced with ones generated from that span.
To continue an incoming trace, make it the ambient context; don't pass these headers yourself. *)
type ('body,'ret) http_request_ =
?verbose:bool ->
?ua:string ->
Expand Down Expand Up @@ -329,12 +332,18 @@ module Http (IO : IO_TYPE) (Curl_IO : CURL with type 'a t = 'a IO.t) : HTTP with
| `Plain, _ -> verbose_curl_result_plain nr_http action t hr
| `Logfmt, _ -> verbose_curl_result_logfmt nr_http action t hr

(* Given a list of strings, check pre-existing entry starting with `~name`; and adds the concatenation of `~name` and `~value` if not. *)
let add_if_absent ~name ~value strs =
match strs with
| Some strs when List.exists (fun s -> Stre.starts_with s (name^":")) strs -> strs
| Some strs -> (String.concat ": " [name; value]) :: strs
| None -> [String.concat ": " [name; value]]
(* Remove from the list of "Name: value" headers those whose name (case-insensitive) is in `names`, then add `pairs`.
Also handles curl's "Name;" syntax for sending an empty header. *)
let replace_headers ~names pairs strs =
let header_name s =
let i = try String.index s ':' with Not_found -> String.length s in
let i = try min i (String.index s ';') with Not_found -> i in
String.strip (String.sub s 0 i)
in
let is_replaced s = let n = header_name s in List.exists (Stre.iequal n) names in
let strs = Option.default [] strs in
let strs = if List.exists is_replaced strs then List.filter (fun s -> not (is_replaced s)) strs else strs in
List.map (fun (n, v) -> sprintf "%s: %s" n v) pairs @ strs
Comment thread
c-cube marked this conversation as resolved.
Resolved

(* NOTE don't forget to set http_1_0=true when sending requests to a Httpev-based server *)
(* Don't use curl_setheaders when using ?headers option *)
Expand Down Expand Up @@ -414,9 +423,10 @@ module Http (IO : IO_TYPE) (Curl_IO : CURL with type 'a t = 'a IO.t) : HTTP with
Possibly_otel.enter_manual_span
~__FUNCTION__:"Devkit.Web.Http.http_request_k" ~__FILE__ ~__LINE__ ~data:describe span_name in

let headers = match Possibly_otel.Traceparent.get_ambient ~explicit_span () with
| None -> headers
| Some value -> Some (add_if_absent ~name:(Possibly_otel.Traceparent.name) ~value headers)
(* trace context headers always override the caller's *)
Comment thread
c-cube marked this conversation as resolved.
Resolved
let headers = match Possibly_otel.Trace_context.get_ambient_headers ~explicit_span () with
| [] -> headers
| pairs -> Some (replace_headers ~names:Possibly_otel.Trace_context.header_names pairs headers)
in

let set_body_and_headers h ct body =
Expand Down
Loading