From a1115661b4bf985ccc144b153da4aa7dca0e5cb3 Mon Sep 17 00:00:00 2001 From: Matt Bray Date: Thu, 24 Mar 2022 13:46:06 +0000 Subject: [PATCH 1/7] wip: cohttp integration --- dune-project | 11 ++++ opentelemetry-cohttp-lwt.opam | 33 +++++++++++ src/integrations/cohttp/README.md | 11 ++++ src/integrations/cohttp/dune | 4 ++ .../cohttp/opentelemetry_cohttp_lwt.ml | 59 +++++++++++++++++++ 5 files changed, 118 insertions(+) create mode 100644 opentelemetry-cohttp-lwt.opam create mode 100644 src/integrations/cohttp/README.md create mode 100644 src/integrations/cohttp/dune create mode 100644 src/integrations/cohttp/opentelemetry_cohttp_lwt.ml diff --git a/dune-project b/dune-project index 21e3f5a2..8d819808 100644 --- a/dune-project +++ b/dune-project @@ -45,3 +45,14 @@ (odoc :with-doc) ocurl) (synopsis "Collector client for opentelemetry, using http + ocurl")) + +(package + (name opentelemetry-cohttp-lwt) + (depends + (ocaml (>= "4.08")) + (dune (>= "2.3")) + (opentelemetry (= :version)) + (opentelemetry-lwt (= :version)) + (odoc :with-doc) + cohttp-lwt) + (synopsis "Opentelemetry tracing for Cohttp HTTP servers")) diff --git a/opentelemetry-cohttp-lwt.opam b/opentelemetry-cohttp-lwt.opam new file mode 100644 index 00000000..af591c09 --- /dev/null +++ b/opentelemetry-cohttp-lwt.opam @@ -0,0 +1,33 @@ +# This file is generated by dune, edit dune-project instead +opam-version: "2.0" +synopsis: "Opentelemetry tracing for Cohttp HTTP servers" +maintainer: ["the Imandra team"] +authors: ["the Imandra team"] +license: "MIT" +homepage: "https://github.com/aestheticintegration/ocaml-opentelemetry" +bug-reports: + "https://github.com/aestheticintegration/ocaml-opentelemetry/issues" +depends: [ + "ocaml" {>= "4.08"} + "dune" {>= "2.3"} + "opentelemetry" {= version} + "opentelemetry-lwt" {= version} + "odoc" {with-doc} + "cohttp-lwt" +] +build: [ + ["dune" "subst"] {pinned} + [ + "dune" + "build" + "-p" + name + "-j" + jobs + "@install" + "@runtest" {with-test} + "@doc" {with-doc} + ] +] +dev-repo: + "git+https://github.com/aestheticintegration/ocaml-opentelemetry.git" diff --git a/src/integrations/cohttp/README.md b/src/integrations/cohttp/README.md new file mode 100644 index 00000000..4b500cde --- /dev/null +++ b/src/integrations/cohttp/README.md @@ -0,0 +1,11 @@ +# Opentelemetry tracing for Cohttp_lwt servers + +Wrap your server callback with `Opentelemetry_cohttp_lwt.trace`: + +```ocaml +let my_server callback = + Cohttp_lwt_unix.Server.create ~mode:(`TCP (`Port 8080)) + (Server.make + ~callback:(Opentelemetry_cohttp_lwt.trace ~service_name:"my-service" callback) + ()) +``` diff --git a/src/integrations/cohttp/dune b/src/integrations/cohttp/dune new file mode 100644 index 00000000..16a92278 --- /dev/null +++ b/src/integrations/cohttp/dune @@ -0,0 +1,4 @@ +(library + (name opentelemetry_cohttp_lwt) + (public_name opentelemetry-cohttp-lwt) + (libraries cohttp-lwt opentelemetry opentelemetry-lwt)) diff --git a/src/integrations/cohttp/opentelemetry_cohttp_lwt.ml b/src/integrations/cohttp/opentelemetry_cohttp_lwt.ml new file mode 100644 index 00000000..cb2f9d40 --- /dev/null +++ b/src/integrations/cohttp/opentelemetry_cohttp_lwt.ml @@ -0,0 +1,59 @@ +open Cohttp +open Cohttp_lwt + +type ('conn, 'body) callback = + 'conn (* Cohttp_lwt_unix.Server.conn *) + -> Request.t + -> 'body (* Cohttp_lwt.Body.t *) + -> (Response.t * 'body) Lwt.t + +let span_attrs (req : Request.t) = + let meth = req |> Request.meth |> Code.string_of_method in + let referer = Header.get (Request.headers req) "referer" in + let host = Header.get (Request.headers req) "host" in + let ua = Header.get (Request.headers req) "user-agent" in + let uri = Request.uri req in + List.concat + [ [ ("http.method", `String meth) ] + ; (match host with None -> [] | Some h -> [ ("http.host", `String h) ]) + ; [ ("http.url", `String (Uri.to_string uri)) ] + ; ( match ua with + | None -> + [] + | Some ua -> + [ ("http.user_agent", `String ua) ] ) + ; ( match referer with + | None -> + [] + | Some r -> + [ ("http.request.header.referer", `String r) ] ) + ] + + +let trace ~service_name (callback : ('conn, 'body) callback ) : ('conn, 'body) callback = + fun conn req body -> + let trace_id = + Header.get (Request.headers req) "trace-id" + |> Option.map (fun s -> + s |> Bytes.of_string |> Opentelemetry.Trace_id.of_bytes ) + in + let parent_id = + Header.get (Request.headers req) "parent-id" + |> Option.map (fun s -> + s |> Bytes.of_string |> Opentelemetry.Span_id.of_bytes ) + in + let open Lwt.Syntax in + Opentelemetry_lwt.Trace.with_ + ~service_name + "request" + ~kind:Span_kind_server + ~attrs:(span_attrs req) + ?parent:parent_id + ?trace_id + (fun scope -> + let* (res, body) = callback conn req body in + Opentelemetry.Trace.add_attrs scope (fun () -> + let code = Response.status res in + let code = Code.code_of_status code in + [ ("http.status_code", `Int code) ]) ; + Lwt.return (res, body) ) From c9dbab94f9157b6329344e6a606d41214970132c Mon Sep 17 00:00:00 2001 From: Matt Bray Date: Thu, 24 Mar 2022 17:44:18 +0000 Subject: [PATCH 2/7] feat(cohttp): read trace context from traceparent header --- .../cohttp/opentelemetry_cohttp_lwt.ml | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/integrations/cohttp/opentelemetry_cohttp_lwt.ml b/src/integrations/cohttp/opentelemetry_cohttp_lwt.ml index cb2f9d40..24b2adb1 100644 --- a/src/integrations/cohttp/opentelemetry_cohttp_lwt.ml +++ b/src/integrations/cohttp/opentelemetry_cohttp_lwt.ml @@ -1,3 +1,5 @@ +module Otel = Opentelemetry +module Otel_lwt = Opentelemetry_lwt open Cohttp open Cohttp_lwt @@ -29,21 +31,20 @@ let span_attrs (req : Request.t) = [ ("http.request.header.referer", `String r) ] ) ] +let trace_context_of_headers req = + let module Traceparent = Otel.Trace_context.Traceparent in + match Header.get (Request.headers req) Traceparent.name with + | None -> None, None + | Some v -> + (match Traceparent.of_value v with + | Ok (trace_id, parent_id) -> (Some trace_id, Some parent_id) + | Error _ -> None, None) let trace ~service_name (callback : ('conn, 'body) callback ) : ('conn, 'body) callback = fun conn req body -> - let trace_id = - Header.get (Request.headers req) "trace-id" - |> Option.map (fun s -> - s |> Bytes.of_string |> Opentelemetry.Trace_id.of_bytes ) - in - let parent_id = - Header.get (Request.headers req) "parent-id" - |> Option.map (fun s -> - s |> Bytes.of_string |> Opentelemetry.Span_id.of_bytes ) - in + let trace_id, parent_id = trace_context_of_headers req in let open Lwt.Syntax in - Opentelemetry_lwt.Trace.with_ + Otel_lwt.Trace.with_ ~service_name "request" ~kind:Span_kind_server @@ -52,7 +53,7 @@ let trace ~service_name (callback : ('conn, 'body) callback ) : ('conn, 'body) c ?trace_id (fun scope -> let* (res, body) = callback conn req body in - Opentelemetry.Trace.add_attrs scope (fun () -> + Otel.Trace.add_attrs scope (fun () -> let code = Response.status res in let code = Code.code_of_status code in [ ("http.status_code", `Int code) ]) ; From 2d62671d07d20de111563b1e1bbfdea2780e5dd3 Mon Sep 17 00:00:00 2001 From: Matt Bray Date: Thu, 24 Mar 2022 17:50:33 +0000 Subject: [PATCH 3/7] refactor(cohttp): wrap in Server module --- src/integrations/cohttp/README.md | 8 +- .../cohttp/opentelemetry_cohttp_lwt.ml | 115 ++++++++++-------- 2 files changed, 67 insertions(+), 56 deletions(-) diff --git a/src/integrations/cohttp/README.md b/src/integrations/cohttp/README.md index 4b500cde..4a25bcad 100644 --- a/src/integrations/cohttp/README.md +++ b/src/integrations/cohttp/README.md @@ -1,11 +1,11 @@ # Opentelemetry tracing for Cohttp_lwt servers -Wrap your server callback with `Opentelemetry_cohttp_lwt.trace`: +Wrap your server callback with `Opentelemetry_cohttp_lwt.Server.trace`: ```ocaml let my_server callback = + let callback = + Opentelemetry_cohttp_lwt.Server.trace ~service_name:"my-service" callback in Cohttp_lwt_unix.Server.create ~mode:(`TCP (`Port 8080)) - (Server.make - ~callback:(Opentelemetry_cohttp_lwt.trace ~service_name:"my-service" callback) - ()) + (Server.make () ~callback) ``` diff --git a/src/integrations/cohttp/opentelemetry_cohttp_lwt.ml b/src/integrations/cohttp/opentelemetry_cohttp_lwt.ml index 24b2adb1..08bbe140 100644 --- a/src/integrations/cohttp/opentelemetry_cohttp_lwt.ml +++ b/src/integrations/cohttp/opentelemetry_cohttp_lwt.ml @@ -3,58 +3,69 @@ module Otel_lwt = Opentelemetry_lwt open Cohttp open Cohttp_lwt -type ('conn, 'body) callback = - 'conn (* Cohttp_lwt_unix.Server.conn *) - -> Request.t - -> 'body (* Cohttp_lwt.Body.t *) - -> (Response.t * 'body) Lwt.t +module Server : sig + (** Trace requests to a Cohttp server. -let span_attrs (req : Request.t) = - let meth = req |> Request.meth |> Code.string_of_method in - let referer = Header.get (Request.headers req) "referer" in - let host = Header.get (Request.headers req) "host" in - let ua = Header.get (Request.headers req) "user-agent" in - let uri = Request.uri req in - List.concat - [ [ ("http.method", `String meth) ] - ; (match host with None -> [] | Some h -> [ ("http.host", `String h) ]) - ; [ ("http.url", `String (Uri.to_string uri)) ] - ; ( match ua with - | None -> - [] - | Some ua -> - [ ("http.user_agent", `String ua) ] ) - ; ( match referer with - | None -> - [] - | Some r -> - [ ("http.request.header.referer", `String r) ] ) - ] + Use it like this: -let trace_context_of_headers req = - let module Traceparent = Otel.Trace_context.Traceparent in - match Header.get (Request.headers req) Traceparent.name with - | None -> None, None - | Some v -> - (match Traceparent.of_value v with - | Ok (trace_id, parent_id) -> (Some trace_id, Some parent_id) - | Error _ -> None, None) + let my_server callback = + let callback = + Opentelemetry_cohttp_lwt.Server.trace ~service_name:"my-service" callback in + Cohttp_lwt_unix.Server.create ~mode:(`TCP (`Port 8080)) + (Server.make () ~callback) + *) + val trace : + service_name:string -> + ('conn -> Request.t -> 'body -> (Response.t * 'body) Lwt.t) -> + 'conn -> Request.t -> 'body -> (Response.t * 'body) Lwt.t +end = struct + let span_attrs (req : Request.t) = + let meth = req |> Request.meth |> Code.string_of_method in + let referer = Header.get (Request.headers req) "referer" in + let host = Header.get (Request.headers req) "host" in + let ua = Header.get (Request.headers req) "user-agent" in + let uri = Request.uri req in + List.concat + [ [ ("http.method", `String meth) ] + ; (match host with None -> [] | Some h -> [ ("http.host", `String h) ]) + ; [ ("http.url", `String (Uri.to_string uri)) ] + ; ( match ua with + | None -> + [] + | Some ua -> + [ ("http.user_agent", `String ua) ] ) + ; ( match referer with + | None -> + [] + | Some r -> + [ ("http.request.header.referer", `String r) ] ) + ] -let trace ~service_name (callback : ('conn, 'body) callback ) : ('conn, 'body) callback = - fun conn req body -> - let trace_id, parent_id = trace_context_of_headers req in - let open Lwt.Syntax in - Otel_lwt.Trace.with_ - ~service_name - "request" - ~kind:Span_kind_server - ~attrs:(span_attrs req) - ?parent:parent_id - ?trace_id - (fun scope -> - let* (res, body) = callback conn req body in - Otel.Trace.add_attrs scope (fun () -> - let code = Response.status res in - let code = Code.code_of_status code in - [ ("http.status_code", `Int code) ]) ; - Lwt.return (res, body) ) + let trace_context_of_headers req = + let module Traceparent = Otel.Trace_context.Traceparent in + match Header.get (Request.headers req) Traceparent.name with + | None -> None, None + | Some v -> + (match Traceparent.of_value v with + | Ok (trace_id, parent_id) -> (Some trace_id, Some parent_id) + | Error _ -> None, None) + + let trace ~service_name callback = + fun conn req body -> + let trace_id, parent_id = trace_context_of_headers req in + let open Lwt.Syntax in + Otel_lwt.Trace.with_ + ~service_name + "request" + ~kind:Span_kind_server + ~attrs:(span_attrs req) + ?parent:parent_id + ?trace_id + (fun scope -> + let* (res, body) = callback conn req body in + Otel.Trace.add_attrs scope (fun () -> + let code = Response.status res in + let code = Code.code_of_status code in + [ ("http.status_code", `Int code) ]) ; + Lwt.return (res, body) ) +end From 75f1612ab0f0e55736aebe92204bbc22c01ac283 Mon Sep 17 00:00:00 2001 From: Matt Bray Date: Thu, 24 Mar 2022 17:56:29 +0000 Subject: [PATCH 4/7] feat(cohttp): pass attrs --- src/integrations/cohttp/opentelemetry_cohttp_lwt.ml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/integrations/cohttp/opentelemetry_cohttp_lwt.ml b/src/integrations/cohttp/opentelemetry_cohttp_lwt.ml index 08bbe140..249cc97e 100644 --- a/src/integrations/cohttp/opentelemetry_cohttp_lwt.ml +++ b/src/integrations/cohttp/opentelemetry_cohttp_lwt.ml @@ -16,6 +16,7 @@ module Server : sig *) val trace : service_name:string -> + ?attrs:Otel.Span.key_value list -> ('conn -> Request.t -> 'body -> (Response.t * 'body) Lwt.t) -> 'conn -> Request.t -> 'body -> (Response.t * 'body) Lwt.t end = struct @@ -50,7 +51,7 @@ end = struct | Ok (trace_id, parent_id) -> (Some trace_id, Some parent_id) | Error _ -> None, None) - let trace ~service_name callback = + let trace ~service_name ?(attrs=[]) callback = fun conn req body -> let trace_id, parent_id = trace_context_of_headers req in let open Lwt.Syntax in @@ -58,7 +59,7 @@ end = struct ~service_name "request" ~kind:Span_kind_server - ~attrs:(span_attrs req) + ~attrs:(attrs @ span_attrs req) ?parent:parent_id ?trace_id (fun scope -> From e17fb1dfb700f082e877447d4c98443690dadd76 Mon Sep 17 00:00:00 2001 From: Matt Bray Date: Thu, 24 Mar 2022 18:12:13 +0000 Subject: [PATCH 5/7] feat(cohttp): pass scope to callback --- .../cohttp/opentelemetry_cohttp_lwt.ml | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/integrations/cohttp/opentelemetry_cohttp_lwt.ml b/src/integrations/cohttp/opentelemetry_cohttp_lwt.ml index 249cc97e..f4aec1e6 100644 --- a/src/integrations/cohttp/opentelemetry_cohttp_lwt.ml +++ b/src/integrations/cohttp/opentelemetry_cohttp_lwt.ml @@ -9,15 +9,19 @@ module Server : sig Use it like this: let my_server callback = - let callback = - Opentelemetry_cohttp_lwt.Server.trace ~service_name:"my-service" callback in - Cohttp_lwt_unix.Server.create ~mode:(`TCP (`Port 8080)) - (Server.make () ~callback) + let callback_traced = + Opentelemetry_cohttp_lwt.Server.trace + ~service_name:"my-service" + (fun _scope -> callback) + in + Cohttp_lwt_unix.Server.create + ~mode:(`TCP (`Port 8080)) + (Server.make () ~callback:callback_traced) *) val trace : service_name:string -> ?attrs:Otel.Span.key_value list -> - ('conn -> Request.t -> 'body -> (Response.t * 'body) Lwt.t) -> + (Otel.Trace.scope -> 'conn -> Request.t -> 'body -> (Response.t * 'body) Lwt.t) -> 'conn -> Request.t -> 'body -> (Response.t * 'body) Lwt.t end = struct let span_attrs (req : Request.t) = @@ -42,7 +46,7 @@ end = struct [ ("http.request.header.referer", `String r) ] ) ] - let trace_context_of_headers req = + let trace_context_of_req req = let module Traceparent = Otel.Trace_context.Traceparent in match Header.get (Request.headers req) Traceparent.name with | None -> None, None @@ -53,7 +57,7 @@ end = struct let trace ~service_name ?(attrs=[]) callback = fun conn req body -> - let trace_id, parent_id = trace_context_of_headers req in + let trace_id, parent_id = trace_context_of_req req in let open Lwt.Syntax in Otel_lwt.Trace.with_ ~service_name @@ -63,7 +67,7 @@ end = struct ?parent:parent_id ?trace_id (fun scope -> - let* (res, body) = callback conn req body in + let* (res, body) = callback scope conn req body in Otel.Trace.add_attrs scope (fun () -> let code = Response.status res in let code = Code.code_of_status code in From 78f2e7bde4cba53b43455e6137f7c527fab3c581 Mon Sep 17 00:00:00 2001 From: Matt Bray Date: Fri, 25 Mar 2022 09:32:34 +0000 Subject: [PATCH 6/7] refactor --- .../cohttp/opentelemetry_cohttp_lwt.ml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/integrations/cohttp/opentelemetry_cohttp_lwt.ml b/src/integrations/cohttp/opentelemetry_cohttp_lwt.ml index f4aec1e6..9ba11d61 100644 --- a/src/integrations/cohttp/opentelemetry_cohttp_lwt.ml +++ b/src/integrations/cohttp/opentelemetry_cohttp_lwt.ml @@ -24,7 +24,7 @@ module Server : sig (Otel.Trace.scope -> 'conn -> Request.t -> 'body -> (Response.t * 'body) Lwt.t) -> 'conn -> Request.t -> 'body -> (Response.t * 'body) Lwt.t end = struct - let span_attrs (req : Request.t) = + let attrs_of_request (req : Request.t) = let meth = req |> Request.meth |> Code.string_of_method in let referer = Header.get (Request.headers req) "referer" in let host = Header.get (Request.headers req) "host" in @@ -46,6 +46,11 @@ end = struct [ ("http.request.header.referer", `String r) ] ) ] + let attrs_of_response (res : Response.t) = + let code = Response.status res in + let code = Code.code_of_status code in + [ ("http.status_code", `Int code) ] + let trace_context_of_req req = let module Traceparent = Otel.Trace_context.Traceparent in match Header.get (Request.headers req) Traceparent.name with @@ -63,14 +68,11 @@ end = struct ~service_name "request" ~kind:Span_kind_server - ~attrs:(attrs @ span_attrs req) + ~attrs:(attrs @ attrs_of_request req) ?parent:parent_id ?trace_id (fun scope -> let* (res, body) = callback scope conn req body in - Otel.Trace.add_attrs scope (fun () -> - let code = Response.status res in - let code = Code.code_of_status code in - [ ("http.status_code", `Int code) ]) ; + Otel.Trace.add_attrs scope (fun () -> attrs_of_response res) ; Lwt.return (res, body) ) end From f44c524ad0f568c8a26cd70bb4c6965aba2c0e3b Mon Sep 17 00:00:00 2001 From: Matt Bray Date: Fri, 25 Mar 2022 10:56:01 +0000 Subject: [PATCH 7/7] feat(cohttp/server): put the trace scope in the request --- .../cohttp/opentelemetry_cohttp_lwt.ml | 91 ++++++++++++++++--- 1 file changed, 80 insertions(+), 11 deletions(-) diff --git a/src/integrations/cohttp/opentelemetry_cohttp_lwt.ml b/src/integrations/cohttp/opentelemetry_cohttp_lwt.ml index 9ba11d61..f6f7e72d 100644 --- a/src/integrations/cohttp/opentelemetry_cohttp_lwt.ml +++ b/src/integrations/cohttp/opentelemetry_cohttp_lwt.ml @@ -21,8 +21,40 @@ module Server : sig val trace : service_name:string -> ?attrs:Otel.Span.key_value list -> - (Otel.Trace.scope -> 'conn -> Request.t -> 'body -> (Response.t * 'body) Lwt.t) -> + ('conn -> Request.t -> 'body -> (Response.t * 'body) Lwt.t) -> 'conn -> Request.t -> 'body -> (Response.t * 'body) Lwt.t + + (** Trace a new internal span. + + Identical to [Opentelemetry_lwt.Trace.with_], but fetches/stores the trace + scope in the [x-ocaml-otel-traceparent] header in the request for + convenience. + *) + val with_: + ?trace_state:string -> + ?service_name:string -> + ?attrs:Otel.Span.key_value list -> + ?kind:Otel.Span.kind -> + ?links:(Otel.Trace_id.t * Otel.Span_id.t * string) list -> + string -> + Request.t -> + (Request.t -> 'a Lwt.t) -> + 'a Lwt.t + + (** Get the tracing scope from the custom [x-ocaml-otel-traceparent] header + added by [trace] and [with_]. + *) + val get_trace_context : ?from:[`Internal | `External] -> Request.t -> Otel.Trace.scope option + + (** Set the tracing scope in the custom [x-ocaml-otel-traceparent] header used + by [trace] and [with_]. + *) + val set_trace_context : Otel.Trace.scope -> Request.t -> Request.t + + (** Strip the custom [x-ocaml-otel-traceparent] header added by [trace] and + [with_]. + *) + val remove_trace_context : Request.t -> Request.t end = struct let attrs_of_request (req : Request.t) = let meth = req |> Request.meth |> Code.string_of_method in @@ -51,28 +83,65 @@ end = struct let code = Code.code_of_status code in [ ("http.status_code", `Int code) ] - let trace_context_of_req req = + let header_x_ocaml_otel_traceparent = "x-ocaml-otel-traceparent" + + let set_trace_context (scope : Otel.Trace.scope) req = let module Traceparent = Otel.Trace_context.Traceparent in - match Header.get (Request.headers req) Traceparent.name with - | None -> None, None + let headers = + Header.add (Request.headers req) header_x_ocaml_otel_traceparent + (Traceparent.to_value ~trace_id:scope.trace_id ~parent_id:scope.span_id ()) + in + { req with headers } + + let get_trace_context ?(from=`Internal) req = + let module Traceparent = Otel.Trace_context.Traceparent in + let name = + match from with + | `Internal -> header_x_ocaml_otel_traceparent + | `External -> Traceparent.name + in + match Header.get (Request.headers req) name with + | None -> None | Some v -> (match Traceparent.of_value v with - | Ok (trace_id, parent_id) -> (Some trace_id, Some parent_id) - | Error _ -> None, None) + | Ok (trace_id, parent_id) -> + (Some Otel.Trace.{ trace_id; span_id = parent_id; events = []; attrs = []}) + | Error _ -> None) + + let remove_trace_context req = + let headers = Header.remove (Request.headers req) header_x_ocaml_otel_traceparent in + { req with headers } let trace ~service_name ?(attrs=[]) callback = fun conn req body -> - let trace_id, parent_id = trace_context_of_req req in - let open Lwt.Syntax in + let scope = get_trace_context ~from:`External req in Otel_lwt.Trace.with_ ~service_name "request" ~kind:Span_kind_server + ?trace_id:(Option.map (fun scope -> scope.Otel.Trace.trace_id) scope) + ?parent:(Option.map (fun scope -> scope.Otel.Trace.span_id) scope) ~attrs:(attrs @ attrs_of_request req) - ?parent:parent_id - ?trace_id (fun scope -> - let* (res, body) = callback scope conn req body in + let open Lwt.Syntax in + let req = set_trace_context scope req in + let* (res, body) = callback conn req body in Otel.Trace.add_attrs scope (fun () -> attrs_of_response res) ; Lwt.return (res, body) ) + + let with_ ?trace_state ?service_name ?attrs ?(kind=Otel.Span.Span_kind_internal) ?links name req (f : Request.t -> 'a Lwt.t) = + let scope = get_trace_context ~from:`Internal req in + Otel_lwt.Trace.with_ + ?trace_state + ?service_name + ?attrs + ~kind + ?trace_id:(Option.map (fun scope -> scope.Otel.Trace.trace_id) scope) + ?parent:(Option.map (fun scope -> scope.Otel.Trace.span_id) scope) + ?links + name + (fun scope -> + let open Lwt.Syntax in + let req = set_trace_context scope req in + f req) end